├── .clang_complete ├── .gcc-flags.json ├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── LICENSE.txt ├── README.md ├── api ├── arduino │ ├── sonoff-minimal.ino.bin │ └── sonoff.ino.bin └── upload-arduino.php ├── arduino ├── espupload.py └── version 2.3.0 │ ├── boards.txt │ ├── platform.txt │ └── tools │ └── sdk │ └── ld │ └── eagle.flash.1m0.ld ├── esp8266.flash.1m0.ld ├── platformio.ini └── sonoff ├── _releasenotes.ino ├── core_esp8266_timer.c ├── core_esp8266_wiring_digital.c ├── core_esp8266_wiring_pwm.c ├── settings.h ├── settings.ino ├── sonoff.ino ├── sonoff_template.h ├── support.h ├── support.ino ├── user_config.h ├── user_config_override.h ├── webserver.ino ├── xdrv_domoticz.ino ├── xdrv_ir_send.ino ├── xdrv_snfled.ino ├── xdrv_snfsc.ino ├── xdrv_wemohue.ino ├── xdrv_ws2812.ino ├── xsns_bh1750.ino ├── xsns_bmp.ino ├── xsns_dht.ino ├── xsns_ds18b20.ino ├── xsns_ds18x20.ino ├── xsns_hlw8012.ino ├── xsns_htu21.ino └── xsns_sht1x.ino /.clang_complete: -------------------------------------------------------------------------------- 1 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\tools\sdk\include 2 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\tools\sdk\lwip\include 3 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\cores\esp8266 4 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\variants\generic 5 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\PubSubClient\src 6 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\SPI 7 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\NeoPixelBus_by_Makuna\src 8 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\IRremoteESP8266 9 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WiFi\src 10 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266HTTPClient\src 11 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266httpUpdate\src 12 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WebServer\src 13 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Ticker 14 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266mDNS 15 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\OneWire 16 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\DNSServer\src 17 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Wire 18 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\ArduinoJson 19 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\sonoff 20 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\ArduinoJson 21 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\ArduinoJson 22 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\IRremoteESP8266 23 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\IRremoteESP8266 24 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\NeoPixelBus_by_Makuna\src 25 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\SPI 26 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\NeoPixelBus_by_Makuna\src 27 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\OneWire 28 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\OneWire 29 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\PubSubClient\src 30 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\lib\PubSubClient\src 31 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\.piolibdeps\ArduinoJson_ID64\src 32 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\.piolibdeps\IRremoteESP8266_ID1089 33 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\.piolibdeps\NeoPixelBus_ID547\src 34 | -IC:\tools\itead\Sonoff-Tasmota-5.1.0\.piolibdeps\PubSubClient_ID89\src 35 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ArduinoOTA 36 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\DNSServer\src 37 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\EEPROM 38 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266AVRISP\src 39 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266HTTPClient\src 40 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266HTTPUpdateServer\src 41 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266SSDP 42 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WebServer\src 43 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WiFi\src 44 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WiFiMesh\src 45 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266httpUpdate\src 46 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266mDNS 47 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Ethernet\src 48 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\GDBStub\src 49 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Hash\src 50 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\SD\src 51 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\SPI 52 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Servo\src 53 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\SoftwareSerial 54 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\TFT_Touch_Shield_V2 55 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Ticker 56 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\Wire 57 | -IC:\Users\mzioni\.platformio\packages\framework-arduinoespressif8266\libraries\esp8266\src 58 | -IC:\Users\mzioni\.platformio\packages\toolchain-xtensa\xtensa-lx106-elf\include 59 | -IC:\Users\mzioni\.platformio\packages\toolchain-xtensa\lib\gcc\xtensa-lx106-elf\4.8.2\include 60 | -IC:\Users\mzioni\.platformio\packages\toolchain-xtensa\lib\gcc\xtensa-lx106-elf\4.8.2\include-fixed 61 | -IC:\Users\mzioni\.platformio\packages\tool-unity 62 | -DARDUINO=20300 63 | -DLWIP_OPEN_SRC 64 | -DF_CPU=80000000L 65 | -D__ets__ 66 | -DICACHE_FLASH 67 | -DPLATFORMIO=30400 68 | -DESP8266 69 | -DARDUINO_ARCH_ESP8266 70 | -DARDUINO_ESP8266_ESP01 71 | -DMQTT_MAX_PACKET_SIZE=512 72 | -------------------------------------------------------------------------------- /.gcc-flags.json: -------------------------------------------------------------------------------- 1 | { 2 | "execPath": "C:/Users/mzioni/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-g++.exe", 3 | "gccDefaultCFlags": "-fsyntax-only -std=gnu99 -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -Os -mlongcalls -mtext-section-literals -falign-functions=4 -ffunction-sections -fdata-sections -DARDUINO=20300 -DLWIP_OPEN_SRC -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DPLATFORMIO=30400 -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DMQTT_MAX_PACKET_SIZE=512 -U__STRICT_ANSI__", 4 | "gccDefaultCppFlags": "-fsyntax-only -fno-rtti -fno-exceptions -std=c++11 -Os -mlongcalls -mtext-section-literals -falign-functions=4 -ffunction-sections -fdata-sections -DARDUINO=20300 -DLWIP_OPEN_SRC -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DPLATFORMIO=30400 -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_ESP01 -DMQTT_MAX_PACKET_SIZE=512 -U__STRICT_ANSI__", 5 | "gccErrorLimit": 15, 6 | "gccIncludePaths": "C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/tools/sdk/include,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lwip/include,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/cores/esp8266,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/variants/generic,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/PubSubClient/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/SPI,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/NeoPixelBus_by_Makuna/src,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/IRremoteESP8266,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266HTTPClient/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266httpUpdate/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WebServer/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Ticker,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266mDNS,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/OneWire,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/DNSServer/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Wire,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/ArduinoJson,C:/tools/itead/Sonoff-Tasmota-5.1.0/sonoff,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/ArduinoJson,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/ArduinoJson,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/IRremoteESP8266,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/IRremoteESP8266,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/NeoPixelBus_by_Makuna/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/SPI,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/NeoPixelBus_by_Makuna/src,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/OneWire,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/OneWire,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/PubSubClient/src,C:/tools/itead/Sonoff-Tasmota-5.1.0/lib/PubSubClient/src,C:/tools/itead/Sonoff-Tasmota-5.1.0/.piolibdeps/ArduinoJson_ID64/src,C:/tools/itead/Sonoff-Tasmota-5.1.0/.piolibdeps/IRremoteESP8266_ID1089,C:/tools/itead/Sonoff-Tasmota-5.1.0/.piolibdeps/NeoPixelBus_ID547/src,C:/tools/itead/Sonoff-Tasmota-5.1.0/.piolibdeps/PubSubClient_ID89/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ArduinoOTA,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/DNSServer/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/EEPROM,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266AVRISP/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266HTTPClient/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266HTTPUpdateServer/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266SSDP,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WebServer/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFiMesh/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266httpUpdate/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266mDNS,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Ethernet/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/GDBStub/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Hash/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/SD/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/SPI,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Servo/src,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/SoftwareSerial,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/TFT_Touch_Shield_V2,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Ticker,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/Wire,C:/Users/mzioni/.platformio/packages/framework-arduinoespressif8266/libraries/esp8266/src,C:/Users/mzioni/.platformio/packages/toolchain-xtensa/xtensa-lx106-elf/include,C:/Users/mzioni/.platformio/packages/toolchain-xtensa/lib/gcc/xtensa-lx106-elf/4.8.2/include,C:/Users/mzioni/.platformio/packages/toolchain-xtensa/lib/gcc/xtensa-lx106-elf/4.8.2/include-fixed,C:/Users/mzioni/.platformio/packages/tool-unity", 7 | "gccSuppressWarnings": false 8 | } 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .piolibdeps 3 | .clang_complete 4 | .gcc-flags.json 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - '2.7' 4 | sudo: false 5 | cache: 6 | directories: 7 | - "~/.platformio" 8 | install: 9 | - pip install -U platformio 10 | 11 | script: 12 | - platformio run 13 | deploy: 14 | provider: releases 15 | api_key: 16 | secure: I5x8qJ5+229qJv5jSnNPGERQHl0NSKvrVHdpVzr/HOo2zeZ/Q5sMhHWo3IBD3AKjGdSlpyNApDwYTaGvenMe+xtUWSSxYIy2ptWAWfkpOtUMx3lI3brZJRt8s98xS5m972SC9mlNT2ZU+i6hZ6srYv2w4nDuyX+j7Q6IGqvYtabxUWzza/Zg0yNpPScvvzscW1CVhdEd5qYH6OKfBfuVOj3ZG4pCycvbejhkUJwbCQ5m8+DEXUol8BKeh92+TPC3jDHXWIStdgLIrmkZ3YWxMQBgQ41QIkaf6X1/0WYEcY0DFW6hlDzg2GbJ8tPRRPC9dfgMs3ZMKJkc7e4x7wMvG2QXQ0aO6e7xTMw41JZ/OrIit0JDHB1M8bWDPUhHwjiCht4W77n7KWFk9sIUDzOdMdd69BIMt5IohtkjnIT2dXekB4xiNvfPLYUa70aOuSHWi3HXVSE1R7RX0brmNf/mH1Pm91uun3UqtIwhrpD0gteQnc0EAlHoOJOazdn3cohrtmECZJo+f+EiqFfEHT2hBrHPEvWknNfxAyPS7jYWKQ7pTMk+y/BUkLyIQkimvNz41azA6sA75nnQrZ+ZJQa+KP2cEObMBs/ekzA45nds1UXpolI1W8QIOxJ/Y10C1yxr6V5a3WWg1H8EbF0HaqiyIeQx/UCz7gl62CbLEDui9PA= 17 | file: ".pioenvs/sonoff/firmware.bin" 18 | skip_cleanup: true 19 | on: 20 | tags: true 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sonoff-evil 2 | sonoff evil firmware PoC - used for demonstration during live demo on exploitation by MQTT 3 | 4 | Tested on Sonoff Basic:
5 | 6 | 7 | 8 | > This firmware was used, with minor consmetic changes, for demonstration during BSidesTLV 2017 conference. 9 | > [View the slide deck](https://www.slideshare.net/moshez/mqtt-iot-explore-exploit-bsidestlv-2017-june-2017) 10 | 11 | # 12 | 13 | Code is based on true-to-date code of: 14 | https://github.com/arendst/Sonoff-Tasmota 15 | ABSTRACT: 16 | ## Sonoff-Tasmota 17 | 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. 18 | 19 | -------------------------------------------------------------------------------- /api/arduino/sonoff-minimal.ino.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalmoz/sonoff-evil/384a26c37abe1d105d41b54c4288a25b58c49d76/api/arduino/sonoff-minimal.ino.bin -------------------------------------------------------------------------------- /api/arduino/sonoff.ino.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalmoz/sonoff-evil/384a26c37abe1d105d41b54c4288a25b58c49d76/api/arduino/sonoff.ino.bin -------------------------------------------------------------------------------- /api/upload-arduino.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /arduino/espupload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # espupload by Theo Arends - 20170103 4 | # 5 | # Uploads binary file to OTA server 6 | # 7 | # Execute: espupload -i -p -f 8 | # 9 | # Needs pycurl 10 | # - pip install pycurl 11 | 12 | import sys 13 | import os 14 | import optparse 15 | import logging 16 | import pycurl 17 | 18 | HOST_ADDR = "domus1" 19 | HOST_PORT = 80 20 | HOST_URL = "/api/upload-arduino.php" 21 | 22 | def upload(hostAddr, hostPort, filename): 23 | url = 'http://%s:%d%s' % (hostAddr, hostPort, HOST_URL) 24 | c = pycurl.Curl() 25 | c.setopt(c.URL, url) 26 | # The "Expect:" is there to suppress "Expect: 100-continue" behaviour that is 27 | # the default in libcurl when posting large bodies (and fails on lighttpd). 28 | c.setopt(c.HTTPHEADER, ["Expect:"]) 29 | c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, filename, )), ]) 30 | c.perform() 31 | c.close() 32 | 33 | def parser(): 34 | parser = optparse.OptionParser( 35 | usage = "%prog [options]", 36 | description = "Upload image to over the air Host server for the esp8266 module with OTA support." 37 | ) 38 | 39 | # destination ip and port 40 | group = optparse.OptionGroup(parser, "Destination") 41 | group.add_option("-i", "--host_ip", 42 | dest = "host_ip", 43 | action = "store", 44 | help = "Host IP Address.", 45 | default = HOST_ADDR 46 | ) 47 | group.add_option("-p", "--host_port", 48 | dest = "host_port", 49 | type = "int", 50 | help = "Host server ota Port. Default 80", 51 | default = HOST_PORT 52 | ) 53 | parser.add_option_group(group) 54 | 55 | # image 56 | group = optparse.OptionGroup(parser, "Image") 57 | group.add_option("-f", "--file", 58 | dest = "image", 59 | help = "Image file.", 60 | metavar="FILE", 61 | default = None 62 | ) 63 | parser.add_option_group(group) 64 | 65 | # output group 66 | group = optparse.OptionGroup(parser, "Output") 67 | group.add_option("-d", "--debug", 68 | dest = "debug", 69 | help = "Show debug output. And override loglevel with debug.", 70 | action = "store_true", 71 | default = False 72 | ) 73 | parser.add_option_group(group) 74 | 75 | (options, args) = parser.parse_args() 76 | 77 | return options 78 | # end parser 79 | 80 | def main(args): 81 | # get options 82 | options = parser() 83 | 84 | # adapt log level 85 | loglevel = logging.WARNING 86 | if (options.debug): 87 | loglevel = logging.DEBUG 88 | # end if 89 | 90 | # logging 91 | logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S') 92 | 93 | logging.debug("Options: %s", str(options)) 94 | 95 | if (not options.host_ip or not options.image): 96 | logging.critical("Not enough arguments.") 97 | 98 | return 1 99 | # end if 100 | 101 | if not os.path.exists(options.image): 102 | logging.critical('Sorry: the file %s does not exist', options.image) 103 | 104 | return 1 105 | # end if 106 | 107 | upload(options.host_ip, options.host_port, options.image) 108 | # end main 109 | 110 | if __name__ == '__main__': 111 | sys.exit(main(sys.argv)) 112 | # end if -------------------------------------------------------------------------------- /arduino/version 2.3.0/platform.txt: -------------------------------------------------------------------------------- 1 | 2 | # ESP8266 platform 3 | # ------------------------------ 4 | 5 | # For more info: 6 | # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification 7 | 8 | name=ESP8266 Modules 9 | version=2.2.0 10 | 11 | 12 | 13 | 14 | compiler.warning_flags=-w 15 | compiler.warning_flags.none=-w 16 | compiler.warning_flags.default= 17 | compiler.warning_flags.more=-Wall 18 | compiler.warning_flags.all=-Wall -Wextra 19 | 20 | build.lwip_lib=-llwip_gcc 21 | build.lwip_flags=-DLWIP_OPEN_SRC 22 | 23 | compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ 24 | compiler.sdk.path={runtime.platform.path}/tools/sdk 25 | 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" 26 | 27 | compiler.c.cmd=xtensa-lx106-elf-gcc 28 | 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 29 | 30 | compiler.S.cmd=xtensa-lx106-elf-gcc 31 | compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls 32 | 33 | 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 34 | 35 | compiler.c.elf.cmd=xtensa-lx106-elf-gcc 36 | compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -laxtls -lsmartconfig -lmesh -lwpa2 {build.lwip_lib} -lstdc++ 37 | 38 | compiler.cpp.cmd=xtensa-lx106-elf-g++ 39 | 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 40 | 41 | compiler.as.cmd=xtensa-lx106-elf-as 42 | 43 | compiler.ar.cmd=xtensa-lx106-elf-ar 44 | compiler.ar.flags=cru 45 | 46 | compiler.elf2hex.cmd=esptool 47 | compiler.elf2hex.flags= 48 | 49 | compiler.size.cmd=xtensa-lx106-elf-size 50 | 51 | compiler.esptool.cmd=esptool 52 | compiler.esptool.cmd.windows=esptool.exe 53 | 54 | # This can be overriden in boards.txt 55 | build.extra_flags=-DESP8266 56 | 57 | # These can be overridden in platform.local.txt 58 | compiler.c.extra_flags= 59 | compiler.c.elf.extra_flags= 60 | compiler.S.extra_flags= 61 | compiler.cpp.extra_flags= 62 | compiler.ar.extra_flags= 63 | compiler.objcopy.eep.extra_flags= 64 | compiler.elf2hex.extra_flags= 65 | 66 | ## generate file with git version number 67 | ## needs bash, git, and echo 68 | 69 | ## windows-compatible version may be added later 70 | 71 | 72 | ## Compile c files 73 | 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}" 74 | 75 | ## Compile c++ files 76 | 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}" 77 | 78 | ## Compile S files 79 | 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}" 80 | 81 | ## Create archives 82 | recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/arduino.ar" "{object_file}" 83 | 84 | ## Combine gc-sections, archives, and objects 85 | 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}" 86 | 87 | ## Create eeprom 88 | recipe.objcopy.eep.pattern= 89 | 90 | ## Create hex 91 | #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" 92 | 93 | 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 94 | 95 | ## Save hex 96 | recipe.output.tmp_file={build.project_name}.bin 97 | recipe.output.save_file={build.project_name}.{build.variant}.bin 98 | 99 | ## Compute size 100 | recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" 101 | recipe.size.regex=^(?:\.irom0\.text|\.text|\.data|\.rodata|)\s+([0-9]+).* 102 | recipe.size.regex.data=^(?:\.data|\.rodata|\.bss)\s+([0-9]+).* 103 | #recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).* 104 | 105 | # ------------------------------ 106 | 107 | tools.esptool.cmd=esptool 108 | tools.esptool.cmd.windows=esptool.exe 109 | tools.esptool.path={runtime.tools.esptool.path} 110 | tools.esptool.network_cmd=python 111 | tools.esptool.network_cmd.windows=python.exe 112 | 113 | tools.esptool.upload.protocol=esp 114 | tools.esptool.upload.params.verbose=-vv 115 | tools.esptool.upload.params.quiet= 116 | 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" 117 | 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" 118 | 119 | tools.mkspiffs.cmd=mkspiffs 120 | tools.mkspiffs.cmd.windows=mkspiffs.exe 121 | tools.mkspiffs.path={runtime.tools.mkspiffs.path} 122 | 123 | tools.espupload.cmd=python 124 | tools.espupload.cmd.windows=python.exe 125 | tools.espupload.path={runtime.platform.path}/tools 126 | 127 | tools.espupload.upload.protocol=espupload 128 | tools.espupload.upload.params.verbose= 129 | tools.espupload.upload.params.quiet= 130 | tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin" 131 | -------------------------------------------------------------------------------- /arduino/version 2.3.0/tools/sdk/ld/eagle.flash.1m0.ld: -------------------------------------------------------------------------------- 1 | /* Flash Split for 1M chips, no SPIFFS */ 2 | /* sketch 999KB */ 3 | /* eeprom 20KB */ 4 | 5 | MEMORY 6 | { 7 | dport0_0_seg : org = 0x3FF00000, len = 0x10 8 | dram0_0_seg : org = 0x3FFE8000, len = 0x14000 9 | iram1_0_seg : org = 0x40100000, len = 0x8000 10 | irom0_0_seg : org = 0x40201010, len = 0xf9ff0 11 | } 12 | 13 | PROVIDE ( _SPIFFS_start = 0x402FB000 ); 14 | PROVIDE ( _SPIFFS_end = 0x402FB000 ); 15 | PROVIDE ( _SPIFFS_page = 0 ); 16 | PROVIDE ( _SPIFFS_block = 0 ); 17 | 18 | INCLUDE "../ld/eagle.app.v6.common.ld" 19 | -------------------------------------------------------------------------------- /esp8266.flash.1m0.ld: -------------------------------------------------------------------------------- 1 | /* Flash Split for 1M chips */ 2 | /* sketch 999KB */ 3 | /* eeprom 20KB */ 4 | 5 | MEMORY 6 | { 7 | dport0_0_seg : org = 0x3FF00000, len = 0x10 8 | dram0_0_seg : org = 0x3FFE8000, len = 0x14000 9 | iram1_0_seg : org = 0x40100000, len = 0x8000 10 | irom0_0_seg : org = 0x40201010, len = 0xf9ff0 11 | } 12 | 13 | PROVIDE ( _SPIFFS_start = 0x402FB000 ); 14 | PROVIDE ( _SPIFFS_end = 0x402FB000 ); 15 | PROVIDE ( _SPIFFS_page = 0 ); 16 | PROVIDE ( _SPIFFS_block = 0 ); 17 | 18 | INCLUDE "esp8266.flash.common.ld" 19 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter, extra scripting 4 | ; Upload options: custom port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; 7 | ; Please visit documentation for the other options and examples 8 | ; http://docs.platformio.org/en/stable/projectconf.html 9 | 10 | [platformio] 11 | src_dir = sonoff 12 | 13 | ; Sonoff et al (ESP8266 based) 14 | [env:sonoff] 15 | platform = espressif8266 16 | framework = arduino 17 | board = esp01_1m 18 | board_flash_mode = qio 19 | build_flags = -Wl,-Tesp8266.flash.1m0.ld -DMQTT_MAX_PACKET_SIZE=512 20 | lib_deps = PubSubClient, NeoPixelBus, IRremoteESP8266, ArduinoJSON 21 | 22 | ; Sonoff Touch and Sonoff 4CH (ESP8285 based) 23 | ;[env:sonoff-touch-4ch] 24 | ;platform = espressif8266 25 | ;framework = arduino 26 | ;board = esp01_1m 27 | ;board_flash_mode = dout 28 | ;build_flags = -Wl,-Tesp8266.flash.1m0.ld -DMQTT_MAX_PACKET_SIZE=512 29 | ;lib_deps = PubSubClient, NeoPixelBus, IRremoteESP8266, ArduinoJSON 30 | -------------------------------------------------------------------------------- /sonoff/core_esp8266_timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | timer.c - Timer1 library for esp8266 3 | 4 | Copyright (c) 2015 Hristo Gochkov. All rights reserved. 5 | This file is part of the esp8266 core for Arduino environment. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #include "wiring_private.h" 22 | #include "pins_arduino.h" 23 | 24 | #include "c_types.h" 25 | #include "ets_sys.h" 26 | 27 | // ------------------------------------------------------------------ - 28 | // timer 1 29 | 30 | static volatile timercallback timer1_user_cb = NULL; 31 | 32 | void ICACHE_RAM_ATTR timer1_isr_handler(void *para){ 33 | (void) para; 34 | if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable 35 | T1I = 0; 36 | if (timer1_user_cb) { 37 | // to make ISR compatible to Arduino AVR model where interrupts are disabled 38 | // we disable them before we call the client ISR 39 | uint32_t savedPS = xt_rsil(15); // stop other interrupts 40 | timer1_user_cb(); 41 | xt_wsr_ps(savedPS); 42 | } 43 | } 44 | 45 | void ICACHE_RAM_ATTR timer1_isr_init(){ 46 | ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL); 47 | } 48 | 49 | void timer1_attachInterrupt(timercallback userFunc) { 50 | timer1_user_cb = userFunc; 51 | ETS_FRC1_INTR_ENABLE(); 52 | } 53 | 54 | void ICACHE_RAM_ATTR timer1_detachInterrupt() { 55 | timer1_user_cb = 0; 56 | TEIE &= ~TEIE1;//edge int disable 57 | ETS_FRC1_INTR_DISABLE(); 58 | } 59 | 60 | void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){ 61 | T1C = (1 << TCTE) | ((divider & 3) << TCPD) | ((int_type & 1) << TCIT) | ((reload & 1) << TCAR); 62 | T1I = 0; 63 | } 64 | 65 | void ICACHE_RAM_ATTR timer1_write(uint32_t ticks){ 66 | T1L = ((ticks)& 0x7FFFFF); 67 | if ((T1C & (1 << TCIT)) == 0) TEIE |= TEIE1;//edge int enable 68 | } 69 | 70 | void ICACHE_RAM_ATTR timer1_disable(){ 71 | T1C = 0; 72 | T1I = 0; 73 | } 74 | 75 | //------------------------------------------------------------------- 76 | // timer 0 77 | 78 | static volatile timercallback timer0_user_cb = NULL; 79 | 80 | void ICACHE_RAM_ATTR timer0_isr_handler(void* para){ 81 | (void) para; 82 | if (timer0_user_cb) { 83 | // to make ISR compatible to Arduino AVR model where interrupts are disabled 84 | // we disable them before we call the client ISR 85 | uint32_t savedPS = xt_rsil(15); // stop other interrupts 86 | timer0_user_cb(); 87 | xt_wsr_ps(savedPS); 88 | } 89 | } 90 | 91 | void timer0_isr_init(){ 92 | ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL); 93 | } 94 | 95 | void timer0_attachInterrupt(timercallback userFunc) { 96 | timer0_user_cb = userFunc; 97 | ETS_CCOMPARE0_ENABLE(); 98 | } 99 | 100 | void ICACHE_RAM_ATTR timer0_detachInterrupt() { 101 | timer0_user_cb = NULL; 102 | ETS_CCOMPARE0_DISABLE(); 103 | } 104 | -------------------------------------------------------------------------------- /sonoff/core_esp8266_wiring_digital.c: -------------------------------------------------------------------------------- 1 | /* 2 | digital.c - wiring digital implementation for esp8266 3 | 4 | Copyright (c) 2015 Hristo Gochkov. All rights reserved. 5 | This file is part of the esp8266 core for Arduino environment. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #define ARDUINO_MAIN 22 | #include "wiring_private.h" 23 | #include "pins_arduino.h" 24 | #include "c_types.h" 25 | #include "eagle_soc.h" 26 | #include "ets_sys.h" 27 | 28 | extern void pwm_stop_pin(uint8_t pin); 29 | 30 | uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10}; 31 | 32 | extern void __pinMode(uint8_t pin, uint8_t mode) { 33 | pwm_stop_pin(pin); 34 | if(pin < 16){ 35 | if(mode == SPECIAL){ 36 | GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) 37 | GPEC = (1 << pin); //Disable 38 | GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin) 39 | if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX 40 | } else if(mode & FUNCTION_0){ 41 | GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) 42 | GPEC = (1 << pin); //Disable 43 | GPF(pin) = GPFFS((mode >> 4) & 0x07); 44 | if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX 45 | } else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){ 46 | GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO 47 | GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) 48 | if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD); 49 | GPES = (1 << pin); //Enable 50 | } else if(mode == INPUT || mode == INPUT_PULLUP){ 51 | GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO 52 | GPEC = (1 << pin); //Disable 53 | GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) 54 | if(mode == INPUT_PULLUP) { 55 | GPF(pin) |= (1 << GPFPU); // Enable Pullup 56 | } 57 | } else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){ 58 | GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO 59 | GPEC = (1 << pin); //Disable 60 | if(mode == WAKEUP_PULLUP) { 61 | GPF(pin) |= (1 << GPFPU); // Enable Pullup 62 | GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED) 63 | } else { 64 | GPF(pin) |= (1 << GPFPD); // Enable Pulldown 65 | GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED) 66 | } 67 | } 68 | } else if(pin == 16){ 69 | GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO 70 | GPC16 = 0; 71 | if(mode == INPUT || mode == INPUT_PULLDOWN_16){ 72 | if(mode == INPUT_PULLDOWN_16){ 73 | GPF16 |= (1 << GP16FPD);//Enable Pulldown 74 | } 75 | GP16E &= ~1; 76 | } else if(mode == OUTPUT){ 77 | GP16E |= 1; 78 | } 79 | } 80 | } 81 | 82 | extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { 83 | pwm_stop_pin(pin); 84 | if(pin < 16){ 85 | if(val) GPOS = (1 << pin); 86 | else GPOC = (1 << pin); 87 | } else if(pin == 16){ 88 | if(val) GP16O |= 1; 89 | else GP16O &= ~1; 90 | } 91 | } 92 | 93 | extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) { 94 | pwm_stop_pin(pin); 95 | if(pin < 16){ 96 | return GPIP(pin); 97 | } else if(pin == 16){ 98 | return GP16I & 0x01; 99 | } 100 | return 0; 101 | } 102 | 103 | /* 104 | GPIO INTERRUPTS 105 | */ 106 | 107 | typedef void (*voidFuncPtr)(void); 108 | 109 | typedef struct { 110 | uint8_t mode; 111 | void (*fn)(void); 112 | } interrupt_handler_t; 113 | 114 | static interrupt_handler_t interrupt_handlers[16]; 115 | static uint32_t interrupt_reg = 0; 116 | 117 | void ICACHE_RAM_ATTR interrupt_handler(void *arg) { 118 | (void) arg; 119 | uint32_t status = GPIE; 120 | GPIEC = status;//clear them interrupts 121 | uint32_t levels = GPI; 122 | if(status == 0 || interrupt_reg == 0) return; 123 | ETS_GPIO_INTR_DISABLE(); 124 | int i = 0; 125 | uint32_t changedbits = status & interrupt_reg; 126 | while(changedbits){ 127 | while(!(changedbits & (1 << i))) i++; 128 | changedbits &= ~(1 << i); 129 | interrupt_handler_t *handler = &interrupt_handlers[i]; 130 | if (handler->fn && 131 | (handler->mode == CHANGE || 132 | (handler->mode & 1) == !!(levels & (1 << i)))) { 133 | // to make ISR compatible to Arduino AVR model where interrupts are disabled 134 | // we disable them before we call the client ISR 135 | uint32_t savedPS = xt_rsil(15); // stop other interrupts 136 | handler->fn(); 137 | xt_wsr_ps(savedPS); 138 | } 139 | } 140 | ETS_GPIO_INTR_ENABLE(); 141 | } 142 | 143 | extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) { 144 | if(pin < 16) { 145 | interrupt_handler_t *handler = &interrupt_handlers[pin]; 146 | handler->mode = mode; 147 | handler->fn = userFunc; 148 | interrupt_reg |= (1 << pin); 149 | GPC(pin) &= ~(0xF << GPCI);//INT mode disabled 150 | GPIEC = (1 << pin); //Clear Interrupt for this pin 151 | GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode" 152 | } 153 | } 154 | 155 | extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { 156 | if(pin < 16) { 157 | GPC(pin) &= ~(0xF << GPCI);//INT mode disabled 158 | GPIEC = (1 << pin); //Clear Interrupt for this pin 159 | interrupt_reg &= ~(1 << pin); 160 | interrupt_handler_t *handler = &interrupt_handlers[pin]; 161 | handler->mode = 0; 162 | handler->fn = 0; 163 | } 164 | } 165 | 166 | void initPins() { 167 | //Disable UART interrupts 168 | system_set_os_print(0); 169 | U0IE = 0; 170 | U1IE = 0; 171 | 172 | for (int i = 0; i <= 5; ++i) { 173 | pinMode(i, INPUT); 174 | } 175 | // pins 6-11 are used for the SPI flash interface 176 | for (int i = 12; i <= 16; ++i) { 177 | pinMode(i, INPUT); 178 | } 179 | 180 | ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); 181 | ETS_GPIO_INTR_ENABLE(); 182 | } 183 | 184 | extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); 185 | extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); 186 | extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"))); 187 | extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); 188 | extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); 189 | -------------------------------------------------------------------------------- /sonoff/core_esp8266_wiring_pwm.c: -------------------------------------------------------------------------------- 1 | /* 2 | pwm.c - analogWrite implementation for esp8266 3 | 4 | Copyright (c) 2015 Hristo Gochkov. All rights reserved. 5 | This file is part of the esp8266 core for Arduino environment. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #include "wiring_private.h" 22 | #include "pins_arduino.h" 23 | #include "c_types.h" 24 | #include "eagle_soc.h" 25 | #include "ets_sys.h" 26 | 27 | #ifndef F_CPU 28 | #define F_CPU 800000000L 29 | #endif 30 | 31 | struct pwm_isr_table { 32 | uint8_t len; 33 | uint16_t steps[17]; 34 | uint32_t masks[17]; 35 | }; 36 | 37 | struct pwm_isr_data { 38 | struct pwm_isr_table tables[2]; 39 | uint8_t active;//0 or 1, which table is active in ISR 40 | }; 41 | 42 | static struct pwm_isr_data _pwm_isr_data; 43 | 44 | uint32_t pwm_mask = 0; 45 | uint16_t pwm_values[17] = {0,}; 46 | uint32_t pwm_freq = 1000; 47 | uint32_t pwm_range = PWMRANGE; 48 | 49 | uint8_t pwm_steps_changed = 0; 50 | uint32_t pwm_multiplier = 0; 51 | 52 | int pwm_sort_array(uint16_t a[], uint16_t al) 53 | { 54 | uint16_t i, j; 55 | for (i = 1; i < al; i++) { 56 | uint16_t tmp = a[i]; 57 | for (j = i; j >= 1 && tmp < a[j-1]; j--) { 58 | a[j] = a[j-1]; 59 | } 60 | a[j] = tmp; 61 | } 62 | int bl = 1; 63 | for(i = 1; i < al; i++) { 64 | if(a[i] != a[i-1]) { 65 | a[bl++] = a[i]; 66 | } 67 | } 68 | return bl; 69 | } 70 | 71 | uint32_t pwm_get_mask(uint16_t value) 72 | { 73 | uint32_t mask = 0; 74 | int i; 75 | for(i=0; i<17; i++) { 76 | if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) { 77 | mask |= (1 << i); 78 | } 79 | } 80 | return mask; 81 | } 82 | 83 | void prep_pwm_steps() 84 | { 85 | if(pwm_mask == 0) { 86 | return; 87 | } 88 | 89 | int pwm_temp_steps_len = 0; 90 | uint16_t pwm_temp_steps[17]; 91 | uint32_t pwm_temp_masks[17]; 92 | uint32_t range = pwm_range; 93 | 94 | if((F_CPU / ESP8266_CLOCK) == 1) { 95 | range /= 2; 96 | } 97 | 98 | int i; 99 | for(i=0; i<17; i++) { 100 | if((pwm_mask & (1 << i)) != 0 && pwm_values[i] != 0) { 101 | pwm_temp_steps[pwm_temp_steps_len++] = pwm_values[i]; 102 | } 103 | } 104 | pwm_temp_steps[pwm_temp_steps_len++] = range; 105 | pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1; 106 | for(i=0; i0; i--) { 110 | pwm_temp_steps[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1]; 111 | } 112 | 113 | pwm_steps_changed = 0; 114 | struct pwm_isr_table *table = &(_pwm_isr_data.tables[!_pwm_isr_data.active]); 115 | table->len = pwm_temp_steps_len; 116 | ets_memcpy(table->steps, pwm_temp_steps, (pwm_temp_steps_len + 1) * 2); 117 | ets_memcpy(table->masks, pwm_temp_masks, pwm_temp_steps_len * 4); 118 | pwm_multiplier = ESP8266_CLOCK/(range * pwm_freq); 119 | pwm_steps_changed = 1; 120 | } 121 | 122 | void ICACHE_RAM_ATTR pwm_timer_isr() //103-138 123 | { 124 | struct pwm_isr_table *table = &(_pwm_isr_data.tables[_pwm_isr_data.active]); 125 | static uint8_t current_step = 0; 126 | TEIE &= ~TEIE1;//14 127 | T1I = 0;//9 128 | if(current_step < table->len) { //20/21 129 | uint32_t mask = table->masks[current_step] & pwm_mask; 130 | if(mask & 0xFFFF) { 131 | GPOC = mask & 0xFFFF; //15/21 132 | } 133 | if(mask & 0x10000) { 134 | GP16O = 0; //6/13 135 | } 136 | current_step++;//1 137 | } else { 138 | current_step = 0;//1 139 | if(pwm_mask == 0) { //12 140 | table->len = 0; 141 | return; 142 | } 143 | if(pwm_mask & 0xFFFF) { 144 | GPOS = pwm_mask & 0xFFFF; //11 145 | } 146 | if(pwm_mask & 0x10000) { 147 | GP16O = 1; //5/13 148 | } 149 | if(pwm_steps_changed) { //12/21 150 | _pwm_isr_data.active = !_pwm_isr_data.active; 151 | table = &(_pwm_isr_data.tables[_pwm_isr_data.active]); 152 | pwm_steps_changed = 0; 153 | } 154 | } 155 | T1L = (table->steps[current_step] * pwm_multiplier);//23 156 | TEIE |= TEIE1;//13 157 | } 158 | 159 | void pwm_start_timer() 160 | { 161 | timer1_disable(); 162 | ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); 163 | ETS_FRC_TIMER1_NMI_INTR_ATTACH(pwm_timer_isr); 164 | timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); 165 | timer1_write(1); 166 | } 167 | 168 | void ICACHE_RAM_ATTR pwm_stop_pin(uint8_t pin) 169 | { 170 | if(pwm_mask){ 171 | pwm_mask &= ~(1 << pin); 172 | if(pwm_mask == 0) { 173 | ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); 174 | timer1_disable(); 175 | timer1_isr_init(); 176 | } 177 | } 178 | } 179 | 180 | extern void __analogWrite(uint8_t pin, int value) 181 | { 182 | bool start_timer = false; 183 | if(value == 0) { 184 | digitalWrite(pin, LOW); 185 | prep_pwm_steps(); 186 | return; 187 | } 188 | if((pwm_mask & (1 << pin)) == 0) { 189 | if(pwm_mask == 0) { 190 | memset(&_pwm_isr_data, 0, sizeof(_pwm_isr_data)); 191 | start_timer = true; 192 | } 193 | pinMode(pin, OUTPUT); 194 | digitalWrite(pin, LOW); 195 | pwm_mask |= (1 << pin); 196 | } 197 | if((F_CPU / ESP8266_CLOCK) == 1) { 198 | value = (value+1) / 2; 199 | } 200 | pwm_values[pin] = value % (pwm_range + 1); 201 | prep_pwm_steps(); 202 | if(start_timer) { 203 | pwm_start_timer(); 204 | } 205 | } 206 | 207 | extern void __analogWriteFreq(uint32_t freq) 208 | { 209 | pwm_freq = freq; 210 | prep_pwm_steps(); 211 | } 212 | 213 | extern void __analogWriteRange(uint32_t range) 214 | { 215 | pwm_range = range; 216 | prep_pwm_steps(); 217 | } 218 | 219 | extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__analogWrite"))); 220 | extern void analogWriteFreq(uint32_t freq) __attribute__ ((weak, alias("__analogWriteFreq"))); 221 | extern void analogWriteRange(uint32_t range) __attribute__ ((weak, alias("__analogWriteRange"))); 222 | -------------------------------------------------------------------------------- /sonoff/settings.h: -------------------------------------------------------------------------------- 1 | /* 2 | settings.h - setting variables for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | typedef union { // Restricted by MISRA-C Rule 18.4 but so usefull... 21 | uint32_t data; // Allow bit manipulation using SetOption 22 | struct { 23 | uint32_t savestate : 1; // bit 0 24 | uint32_t button_restrict : 1; // bit 1 25 | uint32_t value_units : 1; // bit 2 26 | uint32_t mqtt_enabled : 1; 27 | uint32_t mqtt_response : 1; // bit 4 28 | uint32_t mqtt_power_retain : 1; 29 | uint32_t mqtt_button_retain : 1; 30 | uint32_t mqtt_switch_retain : 1; 31 | uint32_t temperature_conversion : 1; // bit 8 32 | uint32_t mqtt_sensor_retain : 1; 33 | uint32_t mqtt_offline : 1; // bit 10 34 | uint32_t spare11 : 1; 35 | uint32_t spare12 : 1; 36 | uint32_t spare13 : 1; 37 | uint32_t spare14 : 1; 38 | uint32_t spare15 : 1; 39 | uint32_t spare16 : 1; 40 | uint32_t spare17 : 1; 41 | uint32_t spare18 : 1; 42 | uint32_t spare19 : 1; 43 | uint32_t spare20 : 1; 44 | uint32_t emulation : 2; 45 | uint32_t energy_resolution : 3; 46 | uint32_t pressure_resolution : 2; 47 | uint32_t humidity_resolution : 2; 48 | uint32_t temperature_resolution : 2; 49 | }; 50 | } sysBitfield; 51 | 52 | struct SYSCFG { 53 | unsigned long cfg_holder; 54 | unsigned long saveFlag; 55 | unsigned long version; 56 | unsigned long bootcount; 57 | sysBitfield flag; // Add flag since 5.0.2 58 | int16_t savedata; 59 | 60 | int8_t timezone; 61 | char otaUrl[101]; 62 | 63 | char mqtt_prefix[3][11]; // was ex_friendlyname[33] until 3.2.5 64 | 65 | byte serial_enable; // Not used (ever) 66 | byte seriallog_level; 67 | uint8_t sta_config; 68 | byte sta_active; 69 | char sta_ssid[2][33]; 70 | char sta_pwd[2][65]; 71 | char hostname[33]; 72 | char syslog_host[33]; 73 | uint16_t syslog_port; 74 | byte syslog_level; 75 | uint8_t webserver; 76 | byte weblog_level; 77 | 78 | char mqtt_fingerprint[60]; 79 | char mqtt_host[33]; 80 | uint16_t mqtt_port; 81 | char mqtt_client[33]; 82 | char mqtt_user[33]; 83 | char mqtt_pwd[33]; 84 | char mqtt_topic[33]; 85 | char button_topic[33]; 86 | char mqtt_grptopic[33]; 87 | char state_text[3][11]; // was ex_mqtt_subtopic[33] until 4.1.1 88 | byte ex_mqtt_button_retain; // Not used since 5.0.2 89 | byte ex_mqtt_power_retain; // Not used since 5.0.2 90 | byte ex_value_units; // Not used since 5.0.2 91 | byte ex_button_restrict; // Not used since 5.0.2 92 | uint16_t tele_period; 93 | 94 | uint8_t power; 95 | uint8_t ledstate; 96 | uint8_t ex_switchmode; // Not used since 3.9.21 97 | 98 | char domoticz_in_topic[33]; 99 | char domoticz_out_topic[33]; 100 | uint16_t domoticz_update_timer; 101 | unsigned long domoticz_relay_idx[4]; 102 | unsigned long domoticz_key_idx[4]; 103 | 104 | unsigned long hlw_pcal; 105 | unsigned long hlw_ucal; 106 | unsigned long hlw_ical; 107 | unsigned long hlw_kWhtoday; 108 | unsigned long hlw_kWhyesterday; 109 | uint16_t hlw_kWhdoy; 110 | uint16_t hlw_pmin; 111 | uint16_t hlw_pmax; 112 | uint16_t hlw_umin; 113 | uint16_t hlw_umax; 114 | uint16_t hlw_imin; 115 | uint16_t hlw_imax; 116 | uint16_t hlw_mpl; // MaxPowerLimit 117 | uint16_t hlw_mplh; // MaxPowerLimitHold 118 | uint16_t hlw_mplw; // MaxPowerLimitWindow 119 | uint16_t hlw_mspl; // MaxSafePowerLimit 120 | uint16_t hlw_msplh; // MaxSafePowerLimitHold 121 | uint16_t hlw_msplw; // MaxSafePowerLimitWindow 122 | uint16_t hlw_mkwh; // MaxEnergy 123 | uint16_t hlw_mkwhs; // MaxEnergyStart 124 | 125 | // 3.0.6 126 | uint16_t mqtt_retry; // was ex_pulsetime until 4.0.4 127 | 128 | // 3.1.1 129 | uint8_t poweronstate; 130 | 131 | // 3.1.6 132 | uint16_t blinktime; 133 | uint16_t blinkcount; 134 | 135 | // 3.2.4 136 | uint16_t ws_pixels; 137 | uint8_t ws_red; 138 | uint8_t ws_green; 139 | uint8_t ws_blue; 140 | uint8_t ws_ledtable; 141 | uint8_t ws_dimmer; 142 | uint8_t ws_fade; 143 | uint8_t ws_speed; 144 | uint8_t ws_scheme; 145 | uint8_t ws_width; 146 | uint16_t ws_wakeup; 147 | 148 | // 3.2.5 149 | char friendlyname[4][33]; 150 | 151 | // 3.2.8 152 | char switch_topic[33]; 153 | byte ex_mqtt_switch_retain; // Not used since 5.0.2 154 | uint8_t ex_mqtt_enabled; // Not used since 5.0.2 155 | 156 | // 3.2.12 157 | uint8_t sleep; 158 | 159 | // 3.9.3 160 | uint16_t domoticz_switch_idx[4]; 161 | uint16_t domoticz_sensor_idx[12]; 162 | uint8_t module; 163 | mytmplt my_module; 164 | uint16_t led_pixels; 165 | uint8_t led_color[5]; 166 | uint8_t led_table; 167 | uint8_t led_dimmer[3]; 168 | uint8_t led_fade; 169 | uint8_t led_speed; 170 | uint8_t led_scheme; 171 | uint8_t led_width; 172 | uint16_t led_wakeup; 173 | 174 | // 3.9.7 175 | uint8_t ex_emulation; // Not used since 5.0.2 176 | 177 | // 3.9.20 178 | char web_password[33]; 179 | 180 | // 3.9.21 181 | uint8_t switchmode[4]; 182 | 183 | // 4.0.4 184 | char ntp_server[3][33]; 185 | uint16_t pulsetime[MAX_PULSETIMERS]; 186 | 187 | // 4.0.7 188 | uint16_t pwmvalue[5]; 189 | 190 | // 4.0.9 191 | uint32_t ip_address[4]; 192 | 193 | // 5.0.4 194 | unsigned long hlw_kWhtotal; 195 | 196 | // 5.0.4a 197 | char mqtt_fulltopic[101]; 198 | 199 | } sysCfg; 200 | 201 | struct RTCMEM { 202 | uint16_t valid; 203 | byte osw_flag; 204 | byte nu1; 205 | unsigned long hlw_kWhtoday; 206 | unsigned long hlw_kWhtotal; 207 | } rtcMem; 208 | 209 | // See issue https://github.com/esp8266/Arduino/issues/2913 210 | #ifdef USE_ADC_VCC 211 | ADC_MODE(ADC_VCC); // Set ADC input for Power Supply Voltage usage 212 | #endif 213 | 214 | -------------------------------------------------------------------------------- /sonoff/settings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | settings.ino - user settings for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /*********************************************************************************************\ 21 | * RTC memory 22 | \*********************************************************************************************/ 23 | 24 | #define RTC_MEM_VALID 0xA55A 25 | 26 | uint32_t _rtcHash = 0; 27 | 28 | uint32_t getRtcHash() 29 | { 30 | uint32_t hash = 0; 31 | uint8_t *bytes = (uint8_t*)&rtcMem; 32 | 33 | for (uint16_t i = 0; i < sizeof(RTCMEM); i++) { 34 | hash += bytes[i]*(i+1); 35 | } 36 | return hash; 37 | } 38 | 39 | void RTC_Save() 40 | { 41 | if (getRtcHash() != _rtcHash) { 42 | rtcMem.valid = RTC_MEM_VALID; 43 | ESP.rtcUserMemoryWrite(100, (uint32_t*)&rtcMem, sizeof(RTCMEM)); 44 | _rtcHash = getRtcHash(); 45 | #ifdef DEBUG_THEO 46 | addLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Save")); 47 | RTC_Dump(); 48 | #endif // DEBUG_THEO 49 | } 50 | } 51 | 52 | void RTC_Load() 53 | { 54 | ESP.rtcUserMemoryRead(100, (uint32_t*)&rtcMem, sizeof(RTCMEM)); 55 | #ifdef DEBUG_THEO 56 | addLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Load")); 57 | RTC_Dump(); 58 | #endif // DEBUG_THEO 59 | if (rtcMem.valid != RTC_MEM_VALID) { 60 | memset(&rtcMem, 0x00, sizeof(RTCMEM)); 61 | rtcMem.valid = RTC_MEM_VALID; 62 | RTC_Save(); 63 | } 64 | _rtcHash = getRtcHash(); 65 | } 66 | 67 | boolean RTC_Valid() 68 | { 69 | return (RTC_MEM_VALID == rtcMem.valid); 70 | } 71 | 72 | #ifdef DEBUG_THEO 73 | void RTC_Dump() 74 | { 75 | #define CFG_COLS 16 76 | 77 | char log[LOGSZ]; 78 | uint16_t idx; 79 | uint16_t maxrow; 80 | uint16_t row; 81 | uint16_t col; 82 | 83 | uint8_t *buffer = (uint8_t *) &rtcMem; 84 | maxrow = ((sizeof(RTCMEM)+CFG_COLS)/CFG_COLS); 85 | 86 | for (row = 0; row < maxrow; row++) { 87 | idx = row * CFG_COLS; 88 | snprintf_P(log, sizeof(log), PSTR("%04X:"), idx); 89 | for (col = 0; col < CFG_COLS; col++) { 90 | if (!(col%4)) { 91 | snprintf_P(log, sizeof(log), PSTR("%s "), log); 92 | } 93 | snprintf_P(log, sizeof(log), PSTR("%s %02X"), log, buffer[idx + col]); 94 | } 95 | snprintf_P(log, sizeof(log), PSTR("%s |"), log); 96 | for (col = 0; col < CFG_COLS; col++) { 97 | // if (!(col%4)) { 98 | // snprintf_P(log, sizeof(log), PSTR("%s "), log); 99 | // } 100 | snprintf_P(log, sizeof(log), PSTR("%s%c"), log, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); 101 | } 102 | snprintf_P(log, sizeof(log), PSTR("%s|"), log); 103 | addLog(LOG_LEVEL_INFO, log); 104 | } 105 | } 106 | #endif // DEBUG_THEO 107 | 108 | /*********************************************************************************************\ 109 | * Config - Flash 110 | \*********************************************************************************************/ 111 | 112 | extern "C" { 113 | #include "spi_flash.h" 114 | } 115 | #include "eboot_command.h" 116 | 117 | extern "C" uint32_t _SPIFFS_end; 118 | 119 | #define SPIFFS_END ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE 120 | 121 | // Version 3.x config 122 | #define CFG_LOCATION_3 SPIFFS_END - 4 123 | 124 | // Version 4.2 config = eeprom area 125 | #define CFG_LOCATION SPIFFS_END // No need for SPIFFS as it uses EEPROM area 126 | 127 | uint32_t _cfgHash = 0; 128 | 129 | /********************************************************************************************/ 130 | /* 131 | * Based on cores/esp8266/Updater.cpp 132 | */ 133 | void setFlashMode(byte option, byte mode) 134 | { 135 | char log[LOGSZ]; 136 | uint8_t *_buffer; 137 | uint32_t address; 138 | 139 | // option 0 - Use absolute address 0 140 | // option 1 - Use OTA/Upgrade relative address 141 | 142 | if (option) { 143 | eboot_command ebcmd; 144 | eboot_command_read(&ebcmd); 145 | address = ebcmd.args[0]; 146 | } else { 147 | address = 0; 148 | } 149 | _buffer = new uint8_t[FLASH_SECTOR_SIZE]; 150 | if (SPI_FLASH_RESULT_OK == spi_flash_read(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { 151 | if (_buffer[2] != mode) { 152 | _buffer[2] = mode &3; 153 | noInterrupts(); 154 | if (SPI_FLASH_RESULT_OK == spi_flash_erase_sector(address / FLASH_SECTOR_SIZE)) { 155 | spi_flash_write(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); 156 | } 157 | interrupts(); 158 | snprintf_P(log, sizeof(log), PSTR("FLSH: Set Flash Mode to %d"), (option) ? mode : ESP.getFlashChipMode()); 159 | addLog(LOG_LEVEL_DEBUG, log); 160 | } 161 | } 162 | delete[] _buffer; 163 | } 164 | 165 | void setModuleFlashMode(byte option) 166 | { 167 | uint8_t mode = 0; // QIO - ESP8266 168 | if ((SONOFF_TOUCH == sysCfg.module) || (SONOFF_4CH == sysCfg.module)) { 169 | mode = 3; // DOUT - ESP8285 170 | } 171 | setFlashMode(option, mode); 172 | } 173 | 174 | uint32_t getHash() 175 | { 176 | uint32_t hash = 0; 177 | uint8_t *bytes = (uint8_t*)&sysCfg; 178 | 179 | for (uint16_t i = 0; i < sizeof(SYSCFG); i++) { 180 | hash += bytes[i]*(i+1); 181 | } 182 | return hash; 183 | } 184 | 185 | /*********************************************************************************************\ 186 | * Config Save - Save parameters to Flash ONLY if any parameter has changed 187 | \*********************************************************************************************/ 188 | 189 | void CFG_Save() 190 | { 191 | char log[LOGSZ]; 192 | 193 | #ifndef BE_MINIMAL 194 | if (getHash() != _cfgHash) { 195 | noInterrupts(); 196 | sysCfg.saveFlag++; 197 | spi_flash_erase_sector(CFG_LOCATION); 198 | spi_flash_write(CFG_LOCATION * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); 199 | interrupts(); 200 | snprintf_P(log, sizeof(log), PSTR("Config: Saved configuration (%d bytes) to flash at %X and count %d"), sizeof(SYSCFG), CFG_LOCATION, sysCfg.saveFlag); 201 | addLog(LOG_LEVEL_DEBUG, log); 202 | _cfgHash = getHash(); 203 | } 204 | #endif // BE_MINIMAL 205 | RTC_Save(); 206 | } 207 | 208 | void CFG_Load() 209 | { 210 | char log[LOGSZ]; 211 | 212 | struct SYSCFGH { 213 | unsigned long cfg_holder; 214 | unsigned long saveFlag; 215 | } _sysCfgH; 216 | 217 | noInterrupts(); 218 | spi_flash_read(CFG_LOCATION * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); 219 | interrupts(); 220 | snprintf_P(log, sizeof(log), PSTR("Config: Loaded configuration from flash at %X and count %d"), CFG_LOCATION, sysCfg.saveFlag); 221 | addLog(LOG_LEVEL_DEBUG, log); 222 | 223 | if (sysCfg.cfg_holder != CFG_HOLDER) { 224 | if ((sysCfg.version < 0x04020000) || (sysCfg.version > 0x06000000)) { 225 | noInterrupts(); 226 | spi_flash_read((CFG_LOCATION_3) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); 227 | spi_flash_read((CFG_LOCATION_3 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&_sysCfgH, sizeof(SYSCFGH)); 228 | if (sysCfg.saveFlag < _sysCfgH.saveFlag) 229 | spi_flash_read((CFG_LOCATION_3 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); 230 | interrupts(); 231 | if (sysCfg.cfg_holder != CFG_HOLDER) { 232 | CFG_Default(); 233 | } else { 234 | sysCfg.saveFlag = 0; 235 | } 236 | } else { 237 | CFG_Default(); 238 | } 239 | } 240 | _cfgHash = getHash(); 241 | 242 | RTC_Load(); 243 | } 244 | 245 | void CFG_Erase() 246 | { 247 | char log[LOGSZ]; 248 | SpiFlashOpResult result; 249 | 250 | uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; 251 | uint32_t _sectorEnd = ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE; 252 | boolean _serialoutput = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); 253 | 254 | snprintf_P(log, sizeof(log), PSTR("Config: Erasing %d flash sectors"), _sectorEnd - _sectorStart); 255 | addLog(LOG_LEVEL_DEBUG, log); 256 | 257 | for (uint32_t _sector = _sectorStart; _sector < _sectorEnd; _sector++) { 258 | noInterrupts(); 259 | result = spi_flash_erase_sector(_sector); 260 | interrupts(); 261 | if (_serialoutput) { 262 | Serial.print(F("Flash: Erased sector ")); 263 | Serial.print(_sector); 264 | if (SPI_FLASH_RESULT_OK == result) { 265 | Serial.println(F(" OK")); 266 | } else { 267 | Serial.println(F(" Error")); 268 | } 269 | delay(10); 270 | } 271 | } 272 | } 273 | 274 | void CFG_Dump(uint16_t srow, uint16_t mrow) 275 | { 276 | #define CFG_COLS 16 277 | 278 | char log[LOGSZ]; 279 | uint16_t idx; 280 | uint16_t maxrow; 281 | uint16_t row; 282 | uint16_t col; 283 | 284 | uint8_t *buffer = (uint8_t *) &sysCfg; 285 | row = 0; 286 | maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); 287 | if ((srow > 0) && (srow < maxrow)) { 288 | row = srow; 289 | } 290 | if (0 == mrow) { // Default only four lines 291 | mrow = 4; 292 | } 293 | if ((mrow > 0) && (mrow < (maxrow - row))) { 294 | maxrow = row + mrow; 295 | } 296 | 297 | for (row = srow; row < maxrow; row++) { 298 | idx = row * CFG_COLS; 299 | snprintf_P(log, sizeof(log), PSTR("%04X:"), idx); 300 | for (col = 0; col < CFG_COLS; col++) { 301 | if (!(col%4)) { 302 | snprintf_P(log, sizeof(log), PSTR("%s "), log); 303 | } 304 | snprintf_P(log, sizeof(log), PSTR("%s %02X"), log, buffer[idx + col]); 305 | } 306 | snprintf_P(log, sizeof(log), PSTR("%s |"), log); 307 | for (col = 0; col < CFG_COLS; col++) { 308 | // if (!(col%4)) { 309 | // snprintf_P(log, sizeof(log), PSTR("%s "), log); 310 | // } 311 | snprintf_P(log, sizeof(log), PSTR("%s%c"), log, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); 312 | } 313 | snprintf_P(log, sizeof(log), PSTR("%s|"), log); 314 | addLog(LOG_LEVEL_INFO, log); 315 | } 316 | } 317 | 318 | /********************************************************************************************/ 319 | 320 | void CFG_Default() 321 | { 322 | addLog_P(LOG_LEVEL_NONE, PSTR("Config: Use default configuration")); 323 | CFG_DefaultSet1(); 324 | CFG_DefaultSet2(); 325 | CFG_Save(); 326 | } 327 | 328 | void CFG_DefaultSet1() 329 | { 330 | memset(&sysCfg, 0x00, sizeof(SYSCFG)); 331 | 332 | sysCfg.cfg_holder = CFG_HOLDER; 333 | // sysCfg.saveFlag = 0; 334 | sysCfg.version = VERSION; 335 | // sysCfg.bootcount = 0; 336 | } 337 | 338 | void CFG_DefaultSet2() 339 | { 340 | memset((char*)&sysCfg +16, 0x00, sizeof(SYSCFG) -16); 341 | 342 | sysCfg.flag.savestate = SAVE_STATE; 343 | sysCfg.savedata = SAVE_DATA; 344 | sysCfg.timezone = APP_TIMEZONE; 345 | strlcpy(sysCfg.otaUrl, OTA_URL, sizeof(sysCfg.otaUrl)); 346 | 347 | sysCfg.seriallog_level = SERIAL_LOG_LEVEL; 348 | // sysCfg.sta_active = 0; 349 | strlcpy(sysCfg.sta_ssid[0], STA_SSID1, sizeof(sysCfg.sta_ssid[0])); 350 | strlcpy(sysCfg.sta_pwd[0], STA_PASS1, sizeof(sysCfg.sta_pwd[0])); 351 | strlcpy(sysCfg.sta_ssid[1], STA_SSID2, sizeof(sysCfg.sta_ssid[1])); 352 | strlcpy(sysCfg.sta_pwd[1], STA_PASS2, sizeof(sysCfg.sta_pwd[1])); 353 | strlcpy(sysCfg.hostname, WIFI_HOSTNAME, sizeof(sysCfg.hostname)); 354 | sysCfg.sta_config = WIFI_CONFIG_TOOL; 355 | strlcpy(sysCfg.syslog_host, SYS_LOG_HOST, sizeof(sysCfg.syslog_host)); 356 | sysCfg.syslog_port = SYS_LOG_PORT; 357 | sysCfg.syslog_level = SYS_LOG_LEVEL; 358 | sysCfg.webserver = WEB_SERVER; 359 | sysCfg.weblog_level = WEB_LOG_LEVEL; 360 | 361 | strlcpy(sysCfg.mqtt_fingerprint, MQTT_FINGERPRINT, sizeof(sysCfg.mqtt_fingerprint)); 362 | strlcpy(sysCfg.mqtt_host, MQTT_HOST, sizeof(sysCfg.mqtt_host)); 363 | sysCfg.mqtt_port = MQTT_PORT; 364 | strlcpy(sysCfg.mqtt_client, MQTT_CLIENT_ID, sizeof(sysCfg.mqtt_client)); 365 | strlcpy(sysCfg.mqtt_user, MQTT_USER, sizeof(sysCfg.mqtt_user)); 366 | strlcpy(sysCfg.mqtt_pwd, MQTT_PASS, sizeof(sysCfg.mqtt_pwd)); 367 | strlcpy(sysCfg.mqtt_topic, MQTT_TOPIC, sizeof(sysCfg.mqtt_topic)); 368 | strlcpy(sysCfg.button_topic, "0", sizeof(sysCfg.button_topic)); 369 | strlcpy(sysCfg.mqtt_grptopic, MQTT_GRPTOPIC, sizeof(sysCfg.mqtt_grptopic)); 370 | sysCfg.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN; 371 | sysCfg.flag.mqtt_power_retain = MQTT_POWER_RETAIN; 372 | // sysCfg.flag.value_units = 0; 373 | // sysCfg.flag.button_restrict = 0; 374 | sysCfg.tele_period = TELE_PERIOD; 375 | 376 | sysCfg.power = APP_POWER; 377 | sysCfg.poweronstate = APP_POWERON_STATE; 378 | sysCfg.ledstate = APP_LEDSTATE; 379 | sysCfg.blinktime = APP_BLINKTIME; 380 | sysCfg.blinkcount = APP_BLINKCOUNT; 381 | sysCfg.sleep = APP_SLEEP; 382 | 383 | strlcpy(sysCfg.domoticz_in_topic, DOMOTICZ_IN_TOPIC, sizeof(sysCfg.domoticz_in_topic)); 384 | strlcpy(sysCfg.domoticz_out_topic, DOMOTICZ_OUT_TOPIC, sizeof(sysCfg.domoticz_out_topic)); 385 | sysCfg.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; 386 | for (byte i = 0; i < 4; i++) { 387 | sysCfg.switchmode[i] = SWITCH_MODE; 388 | // sysCfg.domoticz_relay_idx[i] = 0; 389 | // sysCfg.domoticz_key_idx[i] = 0; 390 | // sysCfg.domoticz_switch_idx[i] = 0; 391 | } 392 | 393 | sysCfg.hlw_pcal = HLW_PREF_PULSE; 394 | sysCfg.hlw_ucal = HLW_UREF_PULSE; 395 | sysCfg.hlw_ical = HLW_IREF_PULSE; 396 | // sysCfg.hlw_kWhtoday = 0; 397 | // sysCfg.hlw_kWhyesterday = 0; 398 | // sysCfg.hlw_kWhdoy = 0; 399 | // sysCfg.hlw_pmin = 0; 400 | // sysCfg.hlw_pmax = 0; 401 | // sysCfg.hlw_umin = 0; 402 | // sysCfg.hlw_umax = 0; 403 | // sysCfg.hlw_imin = 0; 404 | // sysCfg.hlw_imax = 0; 405 | // sysCfg.hlw_mpl = 0; // MaxPowerLimit 406 | sysCfg.hlw_mplh = MAX_POWER_HOLD; 407 | sysCfg.hlw_mplw = MAX_POWER_WINDOW; 408 | // sysCfg.hlw_mspl = 0; // MaxSafePowerLimit 409 | sysCfg.hlw_msplh = SAFE_POWER_HOLD; 410 | sysCfg.hlw_msplw = SAFE_POWER_WINDOW; 411 | // sysCfg.hlw_mkwh = 0; // MaxEnergy 412 | // sysCfg.hlw_mkwhs = 0; // MaxEnergyStart 413 | 414 | CFG_DefaultSet_3_2_4(); 415 | 416 | strlcpy(sysCfg.friendlyname[0], FRIENDLY_NAME, sizeof(sysCfg.friendlyname[0])); 417 | strlcpy(sysCfg.friendlyname[1], FRIENDLY_NAME"2", sizeof(sysCfg.friendlyname[1])); 418 | strlcpy(sysCfg.friendlyname[2], FRIENDLY_NAME"3", sizeof(sysCfg.friendlyname[2])); 419 | strlcpy(sysCfg.friendlyname[3], FRIENDLY_NAME"4", sizeof(sysCfg.friendlyname[3])); 420 | 421 | CFG_DefaultSet_3_9_3(); 422 | 423 | strlcpy(sysCfg.switch_topic, "0", sizeof(sysCfg.switch_topic)); 424 | sysCfg.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN; 425 | sysCfg.flag.mqtt_enabled = MQTT_USE; 426 | 427 | sysCfg.flag.emulation = EMULATION; 428 | 429 | strlcpy(sysCfg.web_password, WEB_PASSWORD, sizeof(sysCfg.web_password)); 430 | 431 | CFG_DefaultSet_4_0_4(); 432 | sysCfg.pulsetime[0] = APP_PULSETIME; 433 | 434 | // 4.0.7 435 | // for (byte i = 0; i < 5; i++) sysCfg.pwmvalue[i] = 0; 436 | 437 | // 4.0.9 438 | CFG_DefaultSet_4_0_9(); 439 | 440 | // 4.1.1 441 | CFG_DefaultSet_4_1_1(); 442 | 443 | // 5.0.2 444 | CFG_DefaultSet_5_0_2(); 445 | 446 | // 5.0.4 447 | // sysCfg.hlw_kWhtotal = 0; 448 | rtcMem.hlw_kWhtotal = 0; 449 | 450 | // 5.0.5 451 | strlcpy(sysCfg.mqtt_fulltopic, MQTT_FULLTOPIC, sizeof(sysCfg.mqtt_fulltopic)); 452 | 453 | // 5.0.6 454 | sysCfg.mqtt_retry = MQTT_RETRY_SECS; 455 | } 456 | 457 | /********************************************************************************************/ 458 | 459 | void CFG_DefaultSet_3_2_4() 460 | { 461 | sysCfg.ws_pixels = WS2812_LEDS; 462 | sysCfg.ws_red = 255; 463 | sysCfg.ws_green = 0; 464 | sysCfg.ws_blue = 0; 465 | sysCfg.ws_ledtable = 0; 466 | sysCfg.ws_dimmer = 8; 467 | sysCfg.ws_fade = 0; 468 | sysCfg.ws_speed = 1; 469 | sysCfg.ws_scheme = 0; 470 | sysCfg.ws_width = 1; 471 | sysCfg.ws_wakeup = 0; 472 | } 473 | 474 | void CFG_DefaultSet_3_9_3() 475 | { 476 | for (byte i = 0; i < 4; i++) { 477 | sysCfg.domoticz_switch_idx[i] = 0; 478 | } 479 | for (byte i = 0; i < 12; i++) { 480 | sysCfg.domoticz_sensor_idx[i] = 0; 481 | } 482 | 483 | sysCfg.module = MODULE; 484 | for (byte i = 0; i < MAX_GPIO_PIN; i++){ 485 | sysCfg.my_module.gp.io[i] = 0; 486 | } 487 | 488 | sysCfg.led_pixels = 0; 489 | for (byte i = 0; i < 5; i++) { 490 | sysCfg.led_color[i] = 255; 491 | } 492 | sysCfg.led_table = 0; 493 | for (byte i = 0; i < 3; i++){ 494 | sysCfg.led_dimmer[i] = 10; 495 | } 496 | sysCfg.led_fade = 0; 497 | sysCfg.led_speed = 0; 498 | sysCfg.led_scheme = 0; 499 | sysCfg.led_width = 0; 500 | sysCfg.led_wakeup = 0; 501 | } 502 | 503 | void CFG_DefaultSet_4_0_4() 504 | { 505 | strlcpy(sysCfg.ntp_server[0], NTP_SERVER1, sizeof(sysCfg.ntp_server[0])); 506 | strlcpy(sysCfg.ntp_server[1], NTP_SERVER2, sizeof(sysCfg.ntp_server[1])); 507 | strlcpy(sysCfg.ntp_server[2], NTP_SERVER3, sizeof(sysCfg.ntp_server[2])); 508 | for (byte j =0; j < 3; j++) { 509 | for (byte i = 0; i < strlen(sysCfg.ntp_server[j]); i++) { 510 | if (sysCfg.ntp_server[j][i] == ',') { 511 | sysCfg.ntp_server[j][i] = '.'; 512 | } 513 | } 514 | } 515 | sysCfg.pulsetime[0] = APP_PULSETIME; 516 | for (byte i = 1; i < MAX_PULSETIMERS; i++) { 517 | sysCfg.pulsetime[i] = 0; 518 | } 519 | } 520 | 521 | void CFG_DefaultSet_4_0_9() 522 | { 523 | strlcpy(sysCfg.mqtt_prefix[0], SUB_PREFIX, sizeof(sysCfg.mqtt_prefix[0])); 524 | strlcpy(sysCfg.mqtt_prefix[1], PUB_PREFIX, sizeof(sysCfg.mqtt_prefix[1])); 525 | strlcpy(sysCfg.mqtt_prefix[2], PUB_PREFIX2, sizeof(sysCfg.mqtt_prefix[2])); 526 | parseIP(&sysCfg.ip_address[0], WIFI_IP_ADDRESS); 527 | parseIP(&sysCfg.ip_address[1], WIFI_GATEWAY); 528 | parseIP(&sysCfg.ip_address[2], WIFI_SUBNETMASK); 529 | parseIP(&sysCfg.ip_address[3], WIFI_DNS); 530 | } 531 | 532 | void CFG_DefaultSet_4_1_1() 533 | { 534 | strlcpy(sysCfg.state_text[0], MQTT_STATUS_OFF, sizeof(sysCfg.state_text[0])); 535 | strlcpy(sysCfg.state_text[1], MQTT_STATUS_ON, sizeof(sysCfg.state_text[1])); 536 | strlcpy(sysCfg.state_text[2], MQTT_CMND_TOGGLE, sizeof(sysCfg.state_text[2])); 537 | } 538 | 539 | void CFG_DefaultSet_5_0_2() 540 | { 541 | sysCfg.flag.temperature_conversion = TEMP_CONVERSION; 542 | sysCfg.flag.temperature_resolution = TEMP_RESOLUTION; 543 | sysCfg.flag.humidity_resolution = HUMIDITY_RESOLUTION; 544 | sysCfg.flag.pressure_resolution = PRESSURE_RESOLUTION; 545 | sysCfg.flag.energy_resolution = ENERGY_RESOLUTION; 546 | } 547 | 548 | /********************************************************************************************/ 549 | 550 | void CFG_Delta() 551 | { 552 | if (sysCfg.version != VERSION) { // Fix version dependent changes 553 | if (sysCfg.version < 0x03010200) { // 3.1.2 - Add parameter 554 | sysCfg.poweronstate = APP_POWERON_STATE; 555 | } 556 | if (sysCfg.version < 0x03010600) { // 3.1.6 - Add parameter 557 | sysCfg.blinktime = APP_BLINKTIME; 558 | sysCfg.blinkcount = APP_BLINKCOUNT; 559 | } 560 | if (sysCfg.version < 0x03020400) { // 3.2.4 - Add parameter 561 | CFG_DefaultSet_3_2_4(); 562 | } 563 | if (sysCfg.version < 0x03020500) { // 3.2.5 - Add parameter 564 | getClient(sysCfg.friendlyname[0], sysCfg.mqtt_client, sizeof(sysCfg.friendlyname[0])); 565 | strlcpy(sysCfg.friendlyname[1], FRIENDLY_NAME"2", sizeof(sysCfg.friendlyname[1])); 566 | strlcpy(sysCfg.friendlyname[2], FRIENDLY_NAME"3", sizeof(sysCfg.friendlyname[2])); 567 | strlcpy(sysCfg.friendlyname[3], FRIENDLY_NAME"4", sizeof(sysCfg.friendlyname[3])); 568 | } 569 | if (sysCfg.version < 0x03020800) { // 3.2.8 - Add parameter 570 | strlcpy(sysCfg.switch_topic, sysCfg.button_topic, sizeof(sysCfg.switch_topic)); 571 | sysCfg.ex_mqtt_switch_retain = MQTT_SWITCH_RETAIN; 572 | sysCfg.ex_mqtt_enabled = MQTT_USE; 573 | } 574 | if (sysCfg.version < 0x03020C00) { // 3.2.12 - Add parameter 575 | sysCfg.sleep = APP_SLEEP; 576 | } 577 | if (sysCfg.version < 0x03090300) { // 3.9.2d - Add parameter 578 | CFG_DefaultSet_3_9_3(); 579 | } 580 | if (sysCfg.version < 0x03090700) { // 3.9.7 - Add parameter 581 | sysCfg.ex_emulation = EMULATION; 582 | } 583 | if (sysCfg.version < 0x03091400) { 584 | strlcpy(sysCfg.web_password, WEB_PASSWORD, sizeof(sysCfg.web_password)); 585 | } 586 | if (sysCfg.version < 0x03091500) { 587 | for (byte i = 0; i < 4; i++) sysCfg.switchmode[i] = sysCfg.ex_switchmode; 588 | } 589 | if (sysCfg.version < 0x04000200) { 590 | sysCfg.ex_button_restrict = 0; 591 | } 592 | if (sysCfg.version < 0x04000400) { 593 | CFG_DefaultSet_4_0_4(); 594 | } 595 | if (sysCfg.version < 0x04000500) { 596 | memmove(sysCfg.my_module.gp.io, sysCfg.my_module.gp.io +1, MAX_GPIO_PIN -1); // move myio 1 byte to front 597 | sysCfg.my_module.gp.io[MAX_GPIO_PIN -1] = 0; // Clear ADC0 598 | } 599 | if (sysCfg.version < 0x04000700) { 600 | for (byte i = 0; i < 5; i++) { 601 | sysCfg.pwmvalue[i] = 0; 602 | } 603 | } 604 | if (sysCfg.version < 0x04000804) { 605 | CFG_DefaultSet_4_0_9(); 606 | } 607 | if (sysCfg.version < 0x04010100) { 608 | CFG_DefaultSet_4_1_1(); 609 | } 610 | if (sysCfg.version < 0x05000105) { 611 | sysCfg.flag = { 0 }; 612 | sysCfg.flag.savestate = SAVE_STATE; 613 | sysCfg.flag.button_restrict = sysCfg.ex_button_restrict; 614 | sysCfg.flag.value_units = sysCfg.ex_value_units; 615 | sysCfg.flag.mqtt_enabled = sysCfg.ex_mqtt_enabled; 616 | // sysCfg.flag.mqtt_response = 0; 617 | sysCfg.flag.mqtt_power_retain = sysCfg.ex_mqtt_power_retain; 618 | sysCfg.flag.mqtt_button_retain = sysCfg.ex_mqtt_button_retain; 619 | sysCfg.flag.mqtt_switch_retain = sysCfg.ex_mqtt_switch_retain; 620 | sysCfg.flag.emulation = sysCfg.ex_emulation; 621 | 622 | CFG_DefaultSet_5_0_2(); 623 | 624 | sysCfg.savedata = SAVE_DATA; 625 | } 626 | if (sysCfg.version < 0x05000400) { 627 | sysCfg.hlw_kWhtotal = 0; 628 | rtcMem.hlw_kWhtotal = 0; 629 | } 630 | if (sysCfg.version < 0x05000500) { 631 | strlcpy(sysCfg.mqtt_fulltopic, MQTT_FULLTOPIC, sizeof(sysCfg.mqtt_fulltopic)); 632 | } 633 | if (sysCfg.version < 0x05000600) { 634 | sysCfg.mqtt_retry = MQTT_RETRY_SECS; 635 | } 636 | sysCfg.version = VERSION; 637 | } 638 | } 639 | 640 | 641 | -------------------------------------------------------------------------------- /sonoff/sonoff_template.h: -------------------------------------------------------------------------------- 1 | /* 2 | sonoff_template.h - template settings for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | // User selectable GPIO functionality 21 | enum upins_t { 22 | GPIO_NONE, // Not used 23 | GPIO_DHT11, // DHT11 24 | GPIO_DHT21, // DHT21, AM2301 25 | GPIO_DHT22, // DHT22, AM2302, AM2321 26 | GPIO_DSB, // Single wire DS18B20 or DS18S20 27 | GPIO_I2C_SCL, // I2C SCL 28 | GPIO_I2C_SDA, // I2C SDA 29 | GPIO_WS2812, // WS2812 Led string 30 | GPIO_IRSEND, // IR remote 31 | GPIO_SWT1, // User connected external switches 32 | GPIO_SWT2, 33 | GPIO_SWT3, 34 | GPIO_SWT4, 35 | GPIO_KEY1, // Button usually connected to GPIO0 36 | GPIO_KEY2, 37 | GPIO_KEY3, 38 | GPIO_KEY4, 39 | GPIO_REL1, // Relays 40 | GPIO_REL2, 41 | GPIO_REL3, 42 | GPIO_REL4, 43 | GPIO_REL1_INV, 44 | GPIO_REL2_INV, 45 | GPIO_REL3_INV, 46 | GPIO_REL4_INV, 47 | GPIO_LED1, // Leds 48 | GPIO_LED2, 49 | GPIO_LED3, 50 | GPIO_LED4, 51 | GPIO_LED1_INV, 52 | GPIO_LED2_INV, 53 | GPIO_LED3_INV, 54 | GPIO_LED4_INV, 55 | GPIO_PWM1, // Sonoff Led Cold 56 | GPIO_PWM2, // Sonoff Led Warm 57 | GPIO_PWM3, // Red (swapped with Blue from original) 58 | GPIO_PWM4, // Green 59 | GPIO_PWM5, // Blue (swapped with Red from original) 60 | GPIO_SENSOR_END }; 61 | 62 | // Text in webpage Module Parameters and commands GPIOS and GPIO 63 | const char sensors[GPIO_SENSOR_END][9] PROGMEM = { 64 | "None", 65 | "DHT11", 66 | "AM2301", 67 | "DHT22", 68 | "DS18x20", 69 | "I2C SCL", 70 | "I2C SDA", 71 | "WS2812", 72 | "IRremote", 73 | "Switch1", 74 | "Switch2", 75 | "Switch3", 76 | "Switch4", 77 | "Button1", 78 | "Button2", 79 | "Button3", 80 | "Button4", 81 | "Relay1", 82 | "Relay2", 83 | "Relay3", 84 | "Relay4", 85 | "Relay1I", 86 | "Relay2I", 87 | "Relay3I", 88 | "Relay4I", 89 | "Led1", 90 | "Led2", 91 | "Led3", 92 | "Led4", 93 | "Led1I", 94 | "Led2I", 95 | "Led3I", 96 | "Led4I", 97 | "PWM1", 98 | "PWM2", 99 | "PWM3", 100 | "PWM4", 101 | "PWM5" 102 | }; 103 | 104 | // Programmer selectable GPIO functionality offset by user selectable GPIOs 105 | enum fpins_t { 106 | GPIO_RXD = GPIO_SENSOR_END, // Serial interface 107 | GPIO_TXD, // Serial interface 108 | GPIO_HLW_SEL, // HLW8012 Sel output (Sonoff Pow) 109 | GPIO_HLW_CF1, // HLW8012 CF1 voltage / current (Sonoff Pow) 110 | GPIO_HLW_CF, // HLW8012 CF power (Sonoff Pow) 111 | GPIO_ADC0, // ADC 112 | GPIO_USER, // User configurable 113 | GPIO_MAX }; 114 | 115 | /********************************************************************************************/ 116 | 117 | // Supported hardware modules 118 | enum module_t { 119 | SONOFF_BASIC, 120 | SONOFF_RF, 121 | SONOFF_SV, 122 | SONOFF_TH, 123 | SONOFF_DUAL, 124 | SONOFF_POW, 125 | SONOFF_4CH, 126 | S20, 127 | SLAMPHER, 128 | SONOFF_TOUCH, 129 | SONOFF_LED, 130 | CH1, 131 | CH4, 132 | MOTOR, 133 | ELECTRODRAGON, 134 | EXS_RELAY, 135 | WION, 136 | WEMOS, 137 | SONOFF_DEV, 138 | H801, 139 | SONOFF_SC, 140 | MAXMODULE }; 141 | 142 | /********************************************************************************************/ 143 | 144 | #define MAX_GPIO_PIN 18 // Number of supported GPIO 145 | 146 | typedef struct MYIO { 147 | uint8_t io[MAX_GPIO_PIN]; 148 | } myio; 149 | 150 | typedef struct MYTMPLT { 151 | char name[15]; 152 | myio gp; 153 | } mytmplt; 154 | 155 | // Default module settings 156 | const mytmplt modules[MAXMODULE] PROGMEM = { 157 | { "Sonoff Basic", // Sonoff Basic (ESP8266) 158 | GPIO_KEY1, // GPIO00 Button 159 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 160 | 0, // GPIO02 161 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 162 | GPIO_USER, // GPIO04 Optional sensor 163 | 0, // GPIO05 164 | 0, // GPIO06 (SD_CLK Flash) 165 | 0, // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) 166 | 0, // GPIO08 (SD_DATA1 Flash QIO/DIO) 167 | 0, // GPIO09 (SD_DATA2 Flash QIO) 168 | 0, // GPIO10 (SD_DATA3 Flash QIO) 169 | 0, // GPIO11 (SD_CMD Flash) 170 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 171 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 172 | GPIO_USER, // GPIO14 Optional sensor 173 | 0, // GPIO15 174 | 0, // GPIO16 175 | 0 // ADC0 Analog input 176 | }, 177 | { "Sonoff RF", // Sonoff RF (ESP8266) 178 | GPIO_KEY1, // GPIO00 Button 179 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 180 | 0, 181 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 182 | GPIO_USER, // GPIO04 Optional sensor 183 | 0, 184 | 0, 0, 0, 0, 0, 0, // Flash connection 185 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 186 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 187 | GPIO_USER, // GPIO14 Optional sensor 188 | 0, 0, 0 189 | }, 190 | { "Sonoff SV", // Sonoff SV (ESP8266) 191 | GPIO_KEY1, // GPIO00 Button 192 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 193 | 0, 194 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 195 | GPIO_USER, // GPIO04 Optional sensor 196 | GPIO_USER, // GPIO05 Optional sensor 197 | 0, 0, 0, 0, 0, 0, // Flash connection 198 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 199 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 200 | GPIO_USER, // GPIO14 Optional sensor 201 | 0, 0, 202 | GPIO_ADC0 // ADC0 Analog input 203 | }, 204 | { "Sonoff TH", // Sonoff TH10/16 (ESP8266) 205 | GPIO_KEY1, // GPIO00 Button 206 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 207 | 0, 208 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 209 | GPIO_USER, // GPIO04 Optional sensor 210 | 0, 211 | 0, 0, 0, 0, 0, 0, // Flash connection 212 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 213 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 214 | GPIO_USER, // GPIO14 Optional sensor 215 | 0, 0, 0 216 | }, 217 | { "Sonoff Dual", // Sonoff Dual (ESP8266) 218 | 0, 219 | GPIO_TXD, // GPIO01 Relay control 220 | 0, 221 | GPIO_RXD, // GPIO03 Relay control 222 | GPIO_USER, // GPIO04 Optional sensor 223 | 0, 224 | 0, 0, 0, 0, 0, 0, // Flash connection 225 | 0, 226 | GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 227 | 0, 0, 0, 0 228 | }, 229 | { "Sonoff Pow", // Sonoff Pow (ESP8266) 230 | GPIO_KEY1, // GPIO00 Button 231 | 0, 0, 0, 0, 232 | GPIO_HLW_SEL, // GPIO05 HLW8012 Sel output 233 | 0, 0, 0, 0, 0, 0, // Flash connection 234 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 235 | GPIO_HLW_CF1, // GPIO13 HLW8012 CF1 voltage / current 236 | GPIO_HLW_CF, // GPIO14 HLW8012 CF power 237 | GPIO_LED1, // GPIO15 Green Led (0 = On, 1 = Off) 238 | 0, 0 239 | }, 240 | { "Sonoff 4CH", // Sonoff 4CH (ESP8285) 241 | GPIO_KEY1, // GPIO00 Button 1 242 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 243 | GPIO_USER, // GPIO02 Optional sensor 244 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 245 | GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On) 246 | GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On) 247 | 0, 0, 0, // Flash connection 248 | GPIO_KEY2, // GPIO09 Button 2 249 | GPIO_KEY3, // GPIO10 Button 3 250 | 0, 251 | GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) 252 | GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 253 | GPIO_KEY4, // GPIO14 Button 4 254 | GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) 255 | 0, 0 256 | }, 257 | { "S20 Socket", // S20 Smart Socket (ESP8266) 258 | GPIO_KEY1, // GPIO00 Button 259 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 260 | 0, 261 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 262 | 0, 0, 263 | 0, 0, 0, 0, 0, 0, // Flash connection 264 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 265 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 266 | 0, 0, 0, 0 267 | }, 268 | { "Slampher", // Slampher (ESP8266) 269 | GPIO_KEY1, // GPIO00 Button 270 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 271 | 0, 272 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 273 | 0, 0, 274 | 0, 0, 0, 0, 0, 0, // Flash connection 275 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 276 | GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 277 | 0, 0, 0, 0 278 | }, 279 | { "Sonoff Touch", // Sonoff Touch (ESP8285) 280 | GPIO_KEY1, // GPIO00 Button 281 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 282 | 0, 283 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 284 | 0, 0, 285 | 0, 0, 0, // Flash connection 286 | 0, 0, 0, 287 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 288 | GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 289 | 0, 0, 0, 0 290 | }, 291 | { "Sonoff LED", // Sonoff LED (ESP8266) 292 | GPIO_KEY1, // GPIO00 Button 293 | 0, 0, 0, 294 | GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) 295 | GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) 296 | 0, 0, 0, 0, 0, 0, // Flash connection 297 | GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) 298 | GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 299 | GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) 300 | GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) 301 | 0, 0 302 | }, 303 | { "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266) 304 | GPIO_KEY1, // GPIO00 Button 305 | 0, 0, 0, 0, 0, 306 | 0, 0, 0, 0, 0, 0, // Flash connection 307 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 308 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 309 | 0, 0, 0, 0 310 | }, 311 | { "4 Channel", // 4 Channel Inching/Latching Relays 312 | 0, 313 | GPIO_TXD, // GPIO01 Relay control 314 | 0, 315 | GPIO_RXD, // GPIO03 Relay control 316 | 0, 0, 317 | 0, 0, 0, 0, 0, 0, // Flash connection 318 | 0, 319 | GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 320 | 0, 0, 0, 0 321 | }, 322 | { "Motor C/AC", // Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) 323 | GPIO_KEY1, // GPIO00 Button 324 | 0, 0, 0, 0, 0, 325 | 0, 0, 0, 0, 0, 0, // Flash connection 326 | GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) 327 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 328 | 0, 0, 0, 0 329 | }, 330 | { "ElectroDragon", // ElectroDragon IoT Relay Board (ESP8266) 331 | GPIO_KEY2, // GPIO00 Button 2 332 | GPIO_USER, // GPIO01 Serial RXD and Optional sensor 333 | GPIO_KEY1, // GPIO02 Button 1 334 | GPIO_USER, // GPIO03 Serial TXD and Optional sensor 335 | GPIO_USER, // GPIO04 Optional sensor 336 | GPIO_USER, // GPIO05 Optional sensor 337 | 0, 0, 0, 0, 0, 0, // Flash connection 338 | GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On) 339 | GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On) 340 | GPIO_USER, // GPIO14 Optional sensor 341 | GPIO_USER, // GPIO15 Optional sensor 342 | GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) 343 | GPIO_ADC0 // ADC0 A0 Analog input 344 | }, 345 | { "EXS Relay", // Latching relay https://ex-store.de/ESP8266-WiFi-Relay-V31 (ESP8266) 346 | // Module Pin 1 VCC 3V3, Module Pin 6 GND 347 | GPIO_KEY1, // GPIO00 Module Pin 8 - Button (firmware flash) 348 | GPIO_USER, // GPIO01 Module Pin 2 = UART0_TXD 349 | GPIO_USER, // GPIO02 Module Pin 7 350 | GPIO_USER, // GPIO03 Module Pin 3 = UART0_RXD 351 | GPIO_USER, // GPIO04 Module Pin 10 352 | GPIO_USER, // GPIO05 Module Pin 9 353 | 0, 0, 0, 0, 0, 0, // Flash connection 354 | GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) 355 | GPIO_REL2, // GPIO13 Relay1 ( 1 = On) 356 | GPIO_USER, // GPIO14 Module Pin 5 357 | 0, 358 | GPIO_USER, // GPIO16 Module Pin 4 359 | 0 360 | }, 361 | { "WiOn", // Indoor Tap https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w (ESP8266) 362 | GPIO_USER, // GPIO00 Optional sensor (pm clock) 363 | 0, 364 | GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) 365 | 0, 0, 0, 366 | 0, 0, 0, 0, 0, 0, // Flash connection 367 | GPIO_USER, // GPIO12 Optional sensor (pm data) 368 | GPIO_KEY1, // GPIO13 Button 369 | 0, 370 | GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 371 | 0, 0 372 | }, 373 | { "WeMos D1 mini", // WeMos and NodeMCU hardware (ESP8266) 374 | GPIO_USER, // GPIO00 D3 Wemos Button Shield 375 | GPIO_USER, // GPIO01 TX Serial RXD 376 | GPIO_USER, // GPIO02 D4 Wemos DHT Shield 377 | GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor 378 | GPIO_USER, // GPIO04 D2 Wemos I2C SDA 379 | GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield 380 | 0, 0, 0, 0, 0, 0, // Flash connection 381 | GPIO_USER, // GPIO12 D6 382 | GPIO_USER, // GPIO13 D7 383 | GPIO_USER, // GPIO14 D5 384 | GPIO_USER, // GPIO15 D8 385 | GPIO_USER, // GPIO16 D0 Wemos Wake 386 | GPIO_ADC0 // ADC0 A0 Analog input 387 | }, 388 | { "Sonoff Dev", // Sonoff Dev (ESP8266) 389 | GPIO_KEY1, // GPIO00 E-FW Button 390 | GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor 391 | 0, // GPIO02 392 | GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor 393 | GPIO_USER, // GPIO04 Optional sensor 394 | GPIO_USER, // GPIO05 Optional sensor 395 | 0, 0, 0, 0, 0, 0, // Flash connection 396 | GPIO_USER, // GPIO12 397 | GPIO_LED1_INV, // GPIO13 BLUE LED 398 | GPIO_USER, // GPIO14 Optional sensor 399 | 0, // GPIO15 400 | 0, // GPIO16 401 | GPIO_ADC0 // ADC0 A0 Analog input 402 | }, 403 | { "H801", // Lixada H801 Wifi (ESP8266) 404 | GPIO_KEY1, // GPIO00 E-FW Button 405 | GPIO_LED1, // GPIO01 Green LED 406 | GPIO_TXD, // GPIO02 RX - Pin next to TX on the PCB 407 | GPIO_RXD, // GPIO03 TX - Pin next to GND on the PCB 408 | GPIO_PWM2, // GPIO04 W2 409 | GPIO_LED2_INV, // GPIO05 Red LED 410 | 0, 0, 0, 0, 0, 0, // Flash connection 411 | GPIO_PWM3, // GPIO12 Blue 412 | GPIO_PWM4, // GPIO13 Green 413 | GPIO_PWM1, // GPIO14 W1 414 | GPIO_PWM5, // GPIO15 Red 415 | 0, 0 416 | }, 417 | { "Sonoff SC", // Sonoff SC (ESP8266) 418 | GPIO_KEY1, // GPIO00 Button 419 | GPIO_TXD, // GPIO01 RXD to ATMEGA328P 420 | GPIO_USER, // GPIO02 Optional sensor 421 | GPIO_RXD, // GPIO03 TXD to ATMEGA328P 422 | 0, 0, 423 | 0, 0, 0, 0, 0, 0, // Flash connection 424 | 0, 425 | GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 426 | 0, 0, 0, 0 427 | } 428 | }; 429 | 430 | -------------------------------------------------------------------------------- /sonoff/support.h: -------------------------------------------------------------------------------- 1 | /* 2 | support.h - parameters used by platformio for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #ifndef __SUPPORT_H__ 25 | #define __SUPPORT_H__ 26 | 27 | #include "user_interface.h" 28 | 29 | /* Function prototypes. */ 30 | void WIFI_wps_status_cb(wps_cb_status status); 31 | 32 | #endif // ifndef __SUPPORT_H__ 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /sonoff/user_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | user_config.h - user specific configuration for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /*********************************************************************************************\ 21 | * ATTENTION: Changes to most PARAMETER defines will only override flash settings if you change 22 | * define CFG_HOLDER. 23 | * Most parameters can be changed online using commands via MQTT, WebConsole or serial 24 | * 25 | * Corresponding MQTT/Serial/Console commands in [brackets] 26 | \*********************************************************************************************/ 27 | 28 | // -- Project ------------------------------------- 29 | #define PROJECT "sonoff" // PROJECT is used as the default topic delimiter and OTA file name 30 | // As an IDE restriction it needs to be the same as the main .ino file 31 | 32 | #define CFG_HOLDER 0x20161209 // [Reset 1] Change this value to load following default configuration parameters 33 | #define SAVE_DATA 1 // [SaveData] Save changed parameters to Flash (0 = disable, 1 - 3600 seconds) 34 | #define SAVE_STATE 1 // [SaveState] Save changed power state to Flash (0 = disable, 1 = enable) 35 | 36 | // -- Wifi ---------------------------------------- 37 | #define WIFI_IP_ADDRESS "0.0.0.0" // [IpAddress1] Set to 0.0.0.0 for using DHCP or IP address 38 | #define WIFI_GATEWAY "1.1.1.1" // {IpAddress2] If not using DHCP set Gateway IP address 39 | #define WIFI_SUBNETMASK "255.255.255.0" // [IpAddress3] If not using DHCP set Network mask 40 | #define WIFI_DNS "1.1.1.1" // [IpAddress4] If not using DHCP set DNS IP address (might be equal to WIFI_GATEWAY) 41 | 42 | #define STA_SSID1 "DemoNetwork" // [Ssid1] Wifi SSID 43 | #define STA_PASS1 "pleasehackmeplease" // [Password1] Wifi password 44 | #define STA_SSID2 "MqttDemo" // [Ssid2] Optional alternate AP Wifi SSID 45 | #define STA_PASS2 "hackmepleaseplease" // [Password2] Optional alternate AP Wifi password 46 | #define WIFI_CONFIG_TOOL WIFI_WPSCONFIG // [WifiConfig] Default tool if wifi fails to connect 47 | // (WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY) 48 | 49 | // -- Syslog -------------------------------------- 50 | #define SYS_LOG_HOST "1.1.1.1" // [LogHost] (Linux) syslog host 51 | #define SYS_LOG_PORT 514 // [LogPort] default syslog UDP port 52 | #define SYS_LOG_LEVEL LOG_LEVEL_NONE // [SysLog] 53 | #define SERIAL_LOG_LEVEL LOG_LEVEL_INFO // [SerialLog] 54 | #define WEB_LOG_LEVEL LOG_LEVEL_INFO // [WebLog] 55 | 56 | // -- Ota ----------------------------------------- 57 | #define OTA_URL "http://domus1:80/api/arduino/" PROJECT ".ino.bin" // [OtaUrl] 58 | 59 | // -- MQTT ---------------------------------------- 60 | #define MQTT_USE 1 // [Mqtt] Select default MQTT use (0 = Off, 1 = On) 61 | // !!! TLS uses a LOT OF MEMORY (20k) so be careful to enable other options at the same time !!! 62 | //#define USE_MQTT_TLS // EXPERIMENTAL Use TLS for MQTT connection (+53k code, +20k mem) - Disable by // 63 | // Needs Fingerprint, TLS Port, UserId and Password 64 | #ifdef USE_MQTT_TLS 65 | #define MQTT_HOST "m20.cloudmqtt.com" // [MqttHost] 66 | #define MQTT_FINGERPRINT "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint] 67 | #define MQTT_PORT 20123 // [MqttPort] MQTT TLS port 68 | #define MQTT_USER "cloudmqttuser" // [MqttUser] Mandatory user 69 | #define MQTT_PASS "cloudmqttpassword" // [MqttPassword] Mandatory password 70 | #else 71 | #define MQTT_HOST "1.2.3.4" // [MqttHost] 72 | #define MQTT_PORT 1883 // [MqttPort] MQTT port (10123 on CloudMQTT) 73 | #define MQTT_USER "DVES_USER" // [MqttUser] Optional user 74 | #define MQTT_PASS "DVES_PASS" // [MqttPassword] Optional password 75 | #endif 76 | 77 | #define MQTT_BUTTON_RETAIN 0 // [ButtonRetain] Button may send retain flag (0 = off, 1 = on) 78 | #define MQTT_POWER_RETAIN 0 // [PowerRetain] Power status message may send retain flag (0 = off, 1 = on) 79 | #define MQTT_SWITCH_RETAIN 0 // [SwitchRetain] Switch may send retain flag (0 = off, 1 = on) 80 | 81 | #define MQTT_STATUS_OFF "OFF" // [StateText1] Command or Status result when turned off (needs to be a string like "0" or "Off") 82 | #define MQTT_STATUS_ON "ON" // [StateText2] Command or Status result when turned on (needs to be a string like "1" or "On") 83 | #define MQTT_CMND_TOGGLE "TOGGLE" // [StateText3] Command to send when toggling (needs to be a string like "2" or "Toggle") 84 | 85 | // -- MQTT topics --------------------------------- 86 | //#define MQTT_FULLTOPIC "tasmota/bedroom/%topic%/%prefix%/" // Up to max 80 characers 87 | #define MQTT_FULLTOPIC "%prefix%/%topic%/" // [FullTopic] Subscribe and Publish full topic name - Legacy topic 88 | 89 | // %prefix% token options 90 | #define SUB_PREFIX "cmnd" // [Prefix1] Sonoff devices subscribe to %prefix%/%topic% being SUB_PREFIX/MQTT_TOPIC and SUB_PREFIX/MQTT_GRPTOPIC 91 | #define PUB_PREFIX "stat" // [Prefix2] Sonoff devices publish to %prefix%/%topic% being PUB_PREFIX/MQTT_TOPIC 92 | #define PUB_PREFIX2 "tele" // [Prefix3] Sonoff devices publish telemetry data to %prefix%/%topic% being PUB_PREFIX2/MQTT_TOPIC/UPTIME, POWER and TIME 93 | // May be named the same as PUB_PREFIX 94 | // %topic% token options (also ButtonTopic and SwitchTopic) 95 | #define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic 96 | #define MQTT_GRPTOPIC "sonoffs" // [GroupTopic] MQTT Group topic 97 | #define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address 98 | 99 | // -- MQTT - Telemetry ---------------------------- 100 | #define TELE_PERIOD 300 // [TelePeriod] Telemetry (0 = disable, 10 - 3600 seconds) 101 | 102 | // -- MQTT - Domoticz ----------------------------- 103 | #define USE_DOMOTICZ // Enable Domoticz (+7k code, +0.3k mem) - Disable by // 104 | #define DOMOTICZ_IN_TOPIC "domoticz/in" // [DomoticzInTopic] 105 | #define DOMOTICZ_OUT_TOPIC "domoticz/out" // [DomoticzOutTopic] 106 | #define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional) 107 | 108 | // -- HTTP ---------------------------------------- 109 | #define USE_WEBSERVER // Enable web server and wifi manager (+62k code, +8k mem) - Disable by // 110 | #define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin) 111 | #define WEB_PORT 80 // Web server Port for User and Admin mode 112 | #define WEB_USERNAME "admin" // Web server Admin mode user name 113 | #define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable) 114 | #define FRIENDLY_NAME "Sonoff" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa 115 | #define USE_EMULATION // Enable Belkin WeMo and Hue Bridge emulation for Alexa (+11k code, +2k mem) 116 | #define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE) 117 | 118 | // -- mDNS ---------------------------------------- 119 | #define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem) - Disable by // 120 | #define WEBSERVER_ADVERTISE // Provide access to webserver by name .local/ 121 | #define MQTT_HOST_DISCOVERY // Find MQTT host server (overrides MQTT_HOST if found) 122 | 123 | // -- Time - Up to three NTP servers in your region 124 | #define NTP_SERVER1 "pool.ntp.org" // [NtpServer1] Select first NTP server by name or IP address (129.250.35.250) 125 | #define NTP_SERVER2 "nl.pool.ntp.org" // [NtpServer2] Select second NTP server by name or IP address (5.39.184.5) 126 | #define NTP_SERVER3 "0.nl.pool.ntp.org" // [NtpServer3] Select third NTP server by name or IP address (93.94.224.67) 127 | 128 | // -- Time - Start Daylight Saving Time and timezone offset from UTC in minutes 129 | #define TIME_DST Last, Sun, Mar, 2, +120 // Last sunday in march at 02:00 +120 minutes 130 | 131 | // -- Time - Start Standard Time and timezone offset from UTC in minutes 132 | #define TIME_STD Last, Sun, Oct, 3, +60 // Last sunday in october 02:00 +60 minutes 133 | 134 | // -- Application --------------------------------- 135 | #define APP_TIMEZONE 1 // [Timezone] +1 hour (Amsterdam) (-12 .. 12 = hours from UTC, 99 = use TIME_DST/TIME_STD) 136 | #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) 137 | #define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled) 138 | #define APP_POWERON_STATE 3 // [PowerOnState] Power On Relay state (0 = Off, 1 = On, 2 = Toggle Saved state, 3 = Saved state) 139 | #define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1 140 | #define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000) 141 | #define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec) 142 | 143 | #define SWITCH_MODE TOGGLE // [SwitchMode] TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON or PUSHBUTTON_INV (the wall switch state) 144 | #define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with 145 | 146 | #define TEMP_CONVERSION 0 // [TempUnit] Return temperature in (0 = Celsius or 1 = Fahrenheit) 147 | #define TEMP_RESOLUTION 1 // [TempRes] Maximum number of decimals (0 - 3) showing sensor Temperature 148 | #define HUMIDITY_RESOLUTION 1 // [HumRes] Maximum number of decimals (0 - 3) showing sensor Humidity 149 | #define PRESSURE_RESOLUTION 1 // [PressRes] Maximum number of decimals (0 - 3) showing sensor Pressure 150 | #define ENERGY_RESOLUTION 3 // [EnergyRes] Maximum number of decimals (0 - 5) showing energy usage in kWh 151 | 152 | // -- Sensor code selection ----------------------- 153 | #define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices 154 | 155 | //#define USE_DS18x20 // Optional using OneWire library for multiple DS18B20 and/or DS18S20 (+2k code) 156 | 157 | #define USE_I2C // I2C using library wire (+10k code, 0.2k mem) - Disable by // 158 | #define USE_BH1750 // Add I2C code for BH1750 sensor 159 | #define USE_BMP // Add I2C code for BMP/BME280 sensor 160 | #define USE_HTU // Add I2C code for HTU21/SI7013/SI7020/SI7021 sensor 161 | #define USE_SHT // Add I2C emulating code for SHT1X sensor 162 | 163 | #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+3k code, 0.3k mem) 164 | // #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) 165 | 166 | #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+8k code, +1k mem) - Disable by // 167 | #define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB) 168 | // #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial TXD) (+1k mem) 169 | // When USE_WS2812_DMA is enabled expect Exceptions on Pow 170 | 171 | /*********************************************************************************************\ 172 | * Compile a minimal version if upgrade memory gets tight ONLY TO BE USED FOR UPGRADE STEP 1! 173 | * To be used as step 1 during upgrade. 174 | * Step 2 is re-compile with option BE_MINIMAL commented out. 175 | * !!! Needed for next release of Arduino/ESP8266 (+22k code, +2k mem) !!! 176 | \*********************************************************************************************/ 177 | 178 | //#define BE_MINIMAL // Minimal version if upgrade memory gets tight (-45k code, -2k mem) 179 | 180 | /*********************************************************************************************\ 181 | * No user configurable items below 182 | \*********************************************************************************************/ 183 | 184 | #if defined(USE_MQTT_TLS) && defined(USE_WEBSERVER) 185 | #error "Select either USE_MQTT_TLS or USE_WEBSERVER as there is just not enough memory to play with" 186 | #endif 187 | 188 | #if (ARDUINO < 10610) 189 | #error "This software is supported with Arduino IDE starting from 1.6.10 and ESP8266 Release 2.3.0" 190 | #endif 191 | -------------------------------------------------------------------------------- /sonoff/user_config_override.h: -------------------------------------------------------------------------------- 1 | /* 2 | user_config_override.h - user configuration overrides user_config.h for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /*****************************************************************************************************\ 21 | * ATTENTION: - Changes to most PARAMETER defines will only override flash settings if you change 22 | * define CFG_HOLDER. 23 | * - Expect compiler warnings when no ifdef/undef/endif sequence is used. 24 | * - You still need to update user_config.h for major defines MODULE and USE_MQTT_TLS. 25 | * - Changing MODULE defines are not being tested for validity as they are in user_config.h. 26 | * - Most parameters can be changed online using commands via MQTT, WebConsole or serial. 27 | * - So I see no use in this but anyway, your on your own. 28 | \*****************************************************************************************************/ 29 | 30 | // Examples 31 | //#ifdef CFG_HOLDER 32 | //#undef CFG_HOLDER 33 | //#endif 34 | //#define CFG_HOLDER 0x20161210 35 | 36 | //#ifdef STA_SSID1 37 | //#undef STA_SSID1 38 | //#endif 39 | //#define STA_SSID1 "yourssid1" 40 | 41 | -------------------------------------------------------------------------------- /sonoff/xdrv_domoticz.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xdrv_domoticz.ino - domoticz support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_DOMOTICZ 21 | 22 | #define DOMOTICZ_MAX_SENSORS 5 23 | 24 | #ifdef USE_WEBSERVER 25 | const char HTTP_FORM_DOMOTICZ[] PROGMEM = 26 | "
 Domoticz parameters 
" 27 | "" 28 | "
" 29 | "" 30 | ""; 31 | const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = 32 | "" 33 | ""; 34 | const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = 35 | ""; 36 | const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = 37 | ""; 38 | const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = 39 | ""; 40 | #endif // USE_WEBSERVER 41 | 42 | const char domoticz_sensors[DOMOTICZ_MAX_SENSORS][14] PROGMEM = 43 | { "Temp", "Temp,Hum", "Temp,Hum,Baro", "Power,Energy", "Illuminance" }; 44 | 45 | int domoticz_update_timer = 0; 46 | byte domoticz_update_flag = 1; 47 | 48 | void mqtt_publishDomoticzPowerState(byte device) 49 | { 50 | char svalue[64]; // was MESSZ 51 | 52 | if (sysCfg.domoticz_relay_idx[device -1] && (strlen(sysCfg.domoticz_in_topic) != 0)) { 53 | if ((device < 1) || (device > Maxdevice)) { 54 | device = 1; 55 | } 56 | 57 | if (SONOFF_LED == sysCfg.module) { 58 | snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"), 59 | sysCfg.domoticz_relay_idx[device -1], sysCfg.led_dimmer[device -1]); 60 | mqtt_publish(sysCfg.domoticz_in_topic, svalue); 61 | } 62 | else if ((1 == device) && (pin[GPIO_WS2812] < 99)) { 63 | snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"), 64 | sysCfg.domoticz_relay_idx[device -1], sysCfg.ws_dimmer); 65 | mqtt_publish(sysCfg.domoticz_in_topic, svalue); 66 | } 67 | snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"\"}"), 68 | sysCfg.domoticz_relay_idx[device -1], (power & (0x01 << (device -1))) ? 1 : 0); 69 | mqtt_publish(sysCfg.domoticz_in_topic, svalue); 70 | } 71 | } 72 | 73 | void domoticz_updatePowerState(byte device) 74 | { 75 | if (domoticz_update_flag) { 76 | mqtt_publishDomoticzPowerState(device); 77 | } 78 | domoticz_update_flag = 1; 79 | } 80 | 81 | void domoticz_mqttUpdate() 82 | { 83 | if ((sysCfg.domoticz_update_timer || domoticz_update_timer) && sysCfg.domoticz_relay_idx[0]) { 84 | domoticz_update_timer--; 85 | if (domoticz_update_timer <= 0) { 86 | domoticz_update_timer = sysCfg.domoticz_update_timer; 87 | for (byte i = 1; i <= Maxdevice; i++) { 88 | mqtt_publishDomoticzPowerState(i); 89 | } 90 | } 91 | } 92 | } 93 | 94 | void domoticz_setUpdateTimer(uint16_t value) 95 | { 96 | domoticz_update_timer = value; 97 | } 98 | 99 | void domoticz_mqttSubscribe() 100 | { 101 | if (sysCfg.domoticz_relay_idx[0] && (strlen(sysCfg.domoticz_out_topic) != 0)) { 102 | char stopic[TOPSZ]; 103 | snprintf_P(stopic, sizeof(stopic), PSTR("%s/#"), sysCfg.domoticz_out_topic); // domoticz topic 104 | mqttClient.subscribe(stopic); 105 | mqttClient.loop(); // Solve LmacRxBlk:1 messages 106 | } 107 | } 108 | 109 | boolean domoticz_update() 110 | { 111 | return domoticz_update_flag; 112 | } 113 | 114 | /* 115 | * ArduinoJSON Domoticz Switch entry used to calculate jsonBuf: JSON_OBJECT_SIZE(11) + 129 = 313 116 | { 117 | "Battery" : 255, 118 | "RSSI" : 12, 119 | "dtype" : "Light/Switch", 120 | "id" : "000140E7", 121 | "idx" : 159, 122 | "name" : "Sonoff1", 123 | "nvalue" : 1, 124 | "stype" : "Switch", 125 | "svalue1" : "0", 126 | "switchType" : "Dimmer", 127 | "unit" : 1 128 | } 129 | */ 130 | 131 | boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf) 132 | { 133 | char log[LOGSZ]; 134 | char stemp1[10]; 135 | char scommand[10]; 136 | unsigned long idx = 0; 137 | int16_t nvalue; 138 | int16_t found = 0; 139 | 140 | domoticz_update_flag = 1; 141 | if (!strncmp(topicBuf, sysCfg.domoticz_out_topic, strlen(sysCfg.domoticz_out_topic)) != 0) { 142 | if (sdataBuf < 20) { 143 | return 1; 144 | } 145 | StaticJsonBuffer<400> jsonBuf; 146 | JsonObject& domoticz = jsonBuf.parseObject(dataBuf); 147 | if (!domoticz.success()) { 148 | return 1; 149 | } 150 | // if (strcmp_P(domoticz["dtype"],PSTR("Light/Switch"))) { 151 | // return 1; 152 | // } 153 | idx = domoticz["idx"]; 154 | nvalue = domoticz["nvalue"]; 155 | 156 | snprintf_P(log, sizeof(log), PSTR("DMTZ: idx %d, nvalue %d"), idx, nvalue); 157 | addLog(LOG_LEVEL_DEBUG_MORE, log); 158 | 159 | if (nvalue >= 0 && nvalue <= 2) { 160 | for (byte i = 0; i < Maxdevice; i++) { 161 | if ((idx > 0) && (idx == sysCfg.domoticz_relay_idx[i])) { 162 | snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); 163 | if (2 == nvalue) { 164 | nvalue = domoticz["svalue1"]; 165 | if ((pin[GPIO_WS2812] < 99) && (sysCfg.ws_dimmer == nvalue)) { 166 | return 1; 167 | } 168 | if ((SONOFF_LED == sysCfg.module) && (sysCfg.led_dimmer[i] == nvalue)) { 169 | return 1; 170 | } 171 | snprintf_P(topicBuf, stopicBuf, PSTR("/DIMMER%s"), (Maxdevice > 1) ? stemp1 : ""); 172 | snprintf_P(dataBuf, sdataBuf, PSTR("%d"), nvalue); 173 | found = 1; 174 | } else { 175 | if (((power >> i) &1) == nvalue) { 176 | return 1; 177 | } 178 | snprintf_P(topicBuf, stopicBuf, PSTR("/POWER%s"), (Maxdevice > 1) ? stemp1 : ""); 179 | snprintf_P(dataBuf, sdataBuf, PSTR("%d"), nvalue); 180 | found = 1; 181 | } 182 | break; 183 | } 184 | } 185 | } 186 | if (!found) { 187 | return 1; 188 | } 189 | 190 | snprintf_P(log, sizeof(log), PSTR("DMTZ: Receive topic %s, data %s"), topicBuf, dataBuf); 191 | addLog(LOG_LEVEL_DEBUG_MORE, log); 192 | 193 | domoticz_update_flag = 0; 194 | } 195 | return 0; 196 | } 197 | 198 | /*********************************************************************************************\ 199 | * Commands 200 | \*********************************************************************************************/ 201 | 202 | boolean domoticz_command(const char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue) 203 | { 204 | boolean serviced = true; 205 | 206 | if (!strncmp_P(type,PSTR("DOMOTICZ"),8)) { 207 | if (!strcmp_P(type +8,PSTR("INTOPIC"))) { 208 | if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_in_topic))) { 209 | strlcpy(sysCfg.domoticz_in_topic, (1 == payload) ? DOMOTICZ_IN_TOPIC : dataBuf, sizeof(sysCfg.domoticz_in_topic)); 210 | restartflag = 2; 211 | } 212 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzInTopic\":\"%s\"}"), sysCfg.domoticz_in_topic); 213 | } 214 | else if (!strcmp_P(type +8,PSTR("OUTTOPIC"))) { 215 | if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_out_topic))) { 216 | strlcpy(sysCfg.domoticz_out_topic, (1 == payload) ? DOMOTICZ_OUT_TOPIC : dataBuf, sizeof(sysCfg.domoticz_out_topic)); 217 | restartflag = 2; 218 | } 219 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzOutTopic\":\"%s\"}"), sysCfg.domoticz_out_topic); 220 | } 221 | else if (!strcmp_P(type +8,PSTR("IDX")) && (index > 0) && (index <= Maxdevice)) { 222 | if ((data_len > 0) && (payload >= 0)) { 223 | sysCfg.domoticz_relay_idx[index -1] = payload; 224 | restartflag = 2; 225 | } 226 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzIdx%d\":%d}"), index, sysCfg.domoticz_relay_idx[index -1]); 227 | } 228 | else if (!strcmp_P(type +8,PSTR("KEYIDX")) && (index > 0) && (index <= Maxdevice)) { 229 | if ((data_len > 0) && (payload >= 0)) { 230 | sysCfg.domoticz_key_idx[index -1] = payload; 231 | } 232 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzKeyIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]); 233 | } 234 | else if (!strcmp_P(type +8,PSTR("SWITCHIDX")) && (index > 0) && (index <= Maxdevice)) { 235 | if ((data_len > 0) && (payload >= 0)) { 236 | sysCfg.domoticz_switch_idx[index -1] = payload; 237 | } 238 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSwitchIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]); 239 | } 240 | else if (!strcmp_P(type +8,PSTR("SENSORIDX")) && (index > 0) && (index <= DOMOTICZ_MAX_SENSORS)) { 241 | if ((data_len > 0) && (payload >= 0)) { 242 | sysCfg.domoticz_sensor_idx[index -1] = payload; 243 | } 244 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSensorIdx%d\":%d}"), index, sysCfg.domoticz_sensor_idx[index -1]); 245 | } 246 | else if (!strcmp_P(type +8,PSTR("UPDATETIMER"))) { 247 | if ((data_len > 0) && (payload >= 0) && (payload < 3601)) { 248 | sysCfg.domoticz_update_timer = payload; 249 | } 250 | snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzUpdateTimer\":%d}"), sysCfg.domoticz_update_timer); 251 | } 252 | else serviced = false; 253 | } 254 | else serviced = false; 255 | return serviced; 256 | } 257 | 258 | boolean domoticz_button(byte key, byte device, byte state, byte svalflg) 259 | { 260 | if ((sysCfg.domoticz_key_idx[device -1] || sysCfg.domoticz_switch_idx[device -1]) && (svalflg)) { 261 | char svalue[80]; // was MESSZ 262 | 263 | snprintf_P(svalue, sizeof(svalue), PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"), 264 | (key) ? sysCfg.domoticz_switch_idx[device -1] : sysCfg.domoticz_key_idx[device -1], (state) ? (2 == state) ? "Toggle" : "On" : "Off"); 265 | mqtt_publish(sysCfg.domoticz_in_topic, svalue); 266 | return 1; 267 | } else { 268 | return 0; 269 | } 270 | } 271 | 272 | /*********************************************************************************************\ 273 | * Sensors 274 | \*********************************************************************************************/ 275 | 276 | uint8_t dom_hum_stat(char *hum) 277 | { 278 | uint8_t h = atoi(hum); 279 | return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; 280 | } 281 | 282 | void dom_sensor(byte idx, char *data) 283 | { 284 | char dmess[64]; 285 | 286 | if (sysCfg.domoticz_sensor_idx[idx] && (strlen(sysCfg.domoticz_in_topic) != 0)) { 287 | snprintf_P(dmess, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"), 288 | sysCfg.domoticz_sensor_idx[idx], data); 289 | mqtt_publish(sysCfg.domoticz_in_topic, dmess); 290 | } 291 | } 292 | 293 | void domoticz_sensor1(char *temp) 294 | { 295 | dom_sensor(0, temp); 296 | } 297 | 298 | void domoticz_sensor2(char *temp, char *hum) 299 | { 300 | char data[16]; 301 | snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temp, hum, dom_hum_stat(hum)); 302 | dom_sensor(1, data); 303 | } 304 | 305 | void domoticz_sensor3(char *temp, char *hum, char *baro) 306 | { 307 | char data[32]; 308 | snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temp, hum, dom_hum_stat(hum), baro); 309 | dom_sensor(2, data); 310 | } 311 | 312 | void domoticz_sensor4(uint16_t power, char *energy) 313 | { 314 | char data[16]; 315 | snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); 316 | dom_sensor(3, data); 317 | } 318 | 319 | void domoticz_sensor5(uint16_t lux) 320 | { 321 | char data[8]; 322 | snprintf_P(data, sizeof(data), PSTR("%d"), lux); 323 | dom_sensor(4, data); 324 | } 325 | 326 | /*********************************************************************************************\ 327 | * Presentation 328 | \*********************************************************************************************/ 329 | 330 | #ifdef USE_WEBSERVER 331 | void handleDomoticz() 332 | { 333 | if (HTTP_USER == _httpflag) { 334 | handleRoot(); 335 | return; 336 | } 337 | addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Domoticz config")); 338 | 339 | char stemp[20]; 340 | 341 | String page = FPSTR(HTTP_HEAD); 342 | page.replace("{v}", "Configure Domoticz"); 343 | page += FPSTR(HTTP_FORM_DOMOTICZ); 344 | page.replace("{d1}", String(sysCfg.domoticz_in_topic)); 345 | page.replace("{d2}", String(sysCfg.domoticz_out_topic)); 346 | for (int i = 0; i < 4; i++) { 347 | if (i < Maxdevice) { 348 | page += FPSTR(HTTP_FORM_DOMOTICZ_RELAY); 349 | page.replace("{2", String((int)sysCfg.domoticz_relay_idx[i])); 350 | page.replace("{3", String((int)sysCfg.domoticz_key_idx[i])); 351 | } 352 | if (pin[GPIO_SWT1 +i] < 99) { 353 | page += FPSTR(HTTP_FORM_DOMOTICZ_SWITCH); 354 | page.replace("{4", String((int)sysCfg.domoticz_switch_idx[i])); 355 | } 356 | page.replace("{1", String(i +1)); 357 | } 358 | for (int i = 0; i < DOMOTICZ_MAX_SENSORS; i++) { 359 | page += FPSTR(HTTP_FORM_DOMOTICZ_SENSOR); 360 | page.replace("{1", String(i +1)); 361 | snprintf_P(stemp, sizeof(stemp), domoticz_sensors[i]); 362 | page.replace("{2", stemp); 363 | page.replace("{5", String((int)sysCfg.domoticz_sensor_idx[i])); 364 | } 365 | page += FPSTR(HTTP_FORM_DOMOTICZ_TIMER); 366 | page.replace("{6", String((int)sysCfg.domoticz_update_timer)); 367 | 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) ")
"); 368 | page += FPSTR(HTTP_FORM_END); 369 | page += FPSTR(HTTP_BTN_CONF); 370 | showPage(page); 371 | } 372 | 373 | void domoticz_saveSettings() 374 | { 375 | char log[LOGSZ], stemp[20]; 376 | 377 | strlcpy(sysCfg.domoticz_in_topic, (!strlen(webServer->arg("it").c_str())) ? DOMOTICZ_IN_TOPIC : webServer->arg("it").c_str(), sizeof(sysCfg.domoticz_in_topic)); 378 | strlcpy(sysCfg.domoticz_out_topic, (!strlen(webServer->arg("ot").c_str())) ? DOMOTICZ_OUT_TOPIC : webServer->arg("ot").c_str(), sizeof(sysCfg.domoticz_out_topic)); 379 | for (byte i = 0; i < 4; i++) { 380 | snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1); 381 | sysCfg.domoticz_relay_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); 382 | snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i +1); 383 | sysCfg.domoticz_key_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); 384 | snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1); 385 | sysCfg.domoticz_switch_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); 386 | } 387 | for (byte i = 0; i < DOMOTICZ_MAX_SENSORS; i++) { 388 | snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1); 389 | sysCfg.domoticz_sensor_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); 390 | } 391 | sysCfg.domoticz_update_timer = (!strlen(webServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(webServer->arg("ut").c_str()); 392 | snprintf_P(log, sizeof(log), PSTR("HTTP: Domoticz in %s, out %s, idx %d, %d, %d, %d, update timer %d"), 393 | sysCfg.domoticz_in_topic, sysCfg.domoticz_out_topic, 394 | sysCfg.domoticz_relay_idx[0], sysCfg.domoticz_relay_idx[1], sysCfg.domoticz_relay_idx[2], sysCfg.domoticz_relay_idx[3], 395 | sysCfg.domoticz_update_timer); 396 | addLog(LOG_LEVEL_INFO, log); 397 | snprintf_P(log, sizeof(log), PSTR("HTTP: key %d, %d, %d, %d, switch %d, %d, %d, %d, sensor %d, %d, %d, %d, %d"), 398 | sysCfg.domoticz_key_idx[0], sysCfg.domoticz_key_idx[1], sysCfg.domoticz_key_idx[2], sysCfg.domoticz_key_idx[3], 399 | sysCfg.domoticz_switch_idx[0], sysCfg.domoticz_switch_idx[1], sysCfg.domoticz_switch_idx[2], sysCfg.domoticz_switch_idx[3], 400 | 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]); 401 | addLog(LOG_LEVEL_INFO, log); 402 | } 403 | #endif // USE_WEBSERVER 404 | #endif // USE_DOMOTICZ 405 | 406 | -------------------------------------------------------------------------------- /sonoff/xdrv_ir_send.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xdrv_ir_send.ino - infra red support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Heiko Krupp and Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_IR_REMOTE 21 | /*********************************************************************************************\ 22 | * IR Remote send using IRremoteESP8266 library 23 | \*********************************************************************************************/ 24 | 25 | #ifndef USE_IR_HVAC 26 | #include 27 | #else 28 | #include 29 | 30 | // HVAC TOSHIBA_ 31 | #define HVAC_TOSHIBA_HDR_MARK 4400 32 | #define HVAC_TOSHIBA_HDR_SPACE 4300 33 | #define HVAC_TOSHIBA_BIT_MARK 543 34 | #define HVAC_TOSHIBA_ONE_SPACE 1623 35 | #define HVAC_MISTUBISHI_ZERO_SPACE 472 36 | #define HVAC_TOSHIBA_RPT_MARK 440 37 | #define HVAC_TOSHIBA_RPT_SPACE 7048 // Above original iremote limit 38 | #define HVAC_TOSHIBA_DATALEN 9 39 | 40 | IRMitsubishiAC *mitsubir = NULL; 41 | 42 | const char FANSPEED[] = "A12345S"; 43 | const char HVACMODE[] = "HDCA"; 44 | #endif 45 | 46 | IRsend *irsend = NULL; 47 | 48 | void ir_send_init(void) 49 | { 50 | irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND 51 | irsend->begin(); 52 | 53 | #ifdef USE_IR_HVAC 54 | mitsubir = new IRMitsubishiAC(pin[GPIO_IRSEND]); 55 | #endif //USE_IR_HVAC 56 | } 57 | 58 | /*********************************************************************************************\ 59 | * Commands 60 | \*********************************************************************************************/ 61 | 62 | /* 63 | * ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 64 | IRsend: 65 | { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } 66 | 67 | IRhvac: 68 | { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } 69 | */ 70 | 71 | boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue) 72 | { 73 | boolean serviced = true; 74 | boolean error = false; 75 | const char *protocol; 76 | uint8_t bits = 0; 77 | uint32_t data = 0; 78 | 79 | const char *HVAC_Mode; 80 | const char *HVAC_FanMode; 81 | const char *HVAC_Vendor; 82 | int HVAC_Temp = 21; 83 | boolean HVAC_Power = true; 84 | 85 | // char log[LOGSZ]; 86 | 87 | if (!strcmp_P(type,PSTR("IRSEND"))) { 88 | if (data_len) { 89 | StaticJsonBuffer<128> jsonBuf; 90 | JsonObject &ir_json = jsonBuf.parseObject(dataBufUc); 91 | if (!ir_json.success()) { 92 | snprintf_P(svalue, ssvalue, PSTR("{\"IRSend\":\"Invalid JSON\"}")); // JSON decode failed 93 | } else { 94 | snprintf_P(svalue, ssvalue, PSTR("{\"IRSend\":\"Done\"}")); 95 | protocol = ir_json["PROTOCOL"]; 96 | bits = ir_json["BITS"]; 97 | data = ir_json["DATA"]; 98 | if (protocol && bits && data) { 99 | if (!strcmp_P(protocol,PSTR("NEC"))) irsend->sendNEC(data, bits); 100 | else if (!strcmp_P(protocol,PSTR("SONY"))) irsend->sendSony(data, bits); 101 | else if (!strcmp_P(protocol,PSTR("RC5"))) irsend->sendRC5(data, bits); 102 | else if (!strcmp_P(protocol,PSTR("RC6"))) irsend->sendRC6(data, bits); 103 | else if (!strcmp_P(protocol,PSTR("DISH"))) irsend->sendDISH(data, bits); 104 | else if (!strcmp_P(protocol,PSTR("JVC"))) irsend->sendJVC(data, bits, 1); 105 | else if (!strcmp_P(protocol,PSTR("SAMSUNG"))) irsend->sendSAMSUNG(data, bits); 106 | else { 107 | snprintf_P(svalue, ssvalue, PSTR("{\"IRSend\":\"Protocol not supported\"}")); 108 | } 109 | } else error = true; 110 | } 111 | } else error = true; 112 | if (error) { 113 | snprintf_P(svalue, ssvalue, PSTR("{\"IRSend\":\"No protocol, bits or data\"}")); 114 | } 115 | } 116 | #ifdef USE_IR_HVAC 117 | else if (!strcmp_P(type,PSTR("IRHVAC"))) { 118 | if (data_len) { 119 | StaticJsonBuffer<164> jsonBufer; 120 | JsonObject &root = jsonBufer.parseObject(dataBufUc); 121 | if (!root.success()) { 122 | snprintf_P(svalue, ssvalue, PSTR("{\"IRHVAC\":\"Invalid JSON\"}")); // JSON decode failed 123 | } else { 124 | snprintf_P(svalue, ssvalue, PSTR("{\"IRHVAC\":\"Done\"}")); 125 | HVAC_Vendor = root["VENDOR"]; 126 | HVAC_Power = root["POWER"]; 127 | HVAC_Mode = root["MODE"]; 128 | HVAC_FanMode = root["FANSPEED"]; 129 | HVAC_Temp = root["TEMP"]; 130 | 131 | // snprintf_P(log, sizeof(log), PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), 132 | // HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); 133 | // addLog(LOG_LEVEL_DEBUG, log); 134 | 135 | if (HVAC_Vendor == NULL || !strcmp_P(HVAC_Vendor,PSTR("TOSHIBA"))) { 136 | error = ir_hvac_toshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); 137 | } 138 | else if (!strcmp_P(HVAC_Vendor,PSTR("MITSUBISHI"))) { 139 | error = ir_hvac_mitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); 140 | } 141 | else error = true; 142 | } 143 | } else error = true; 144 | if (error) { 145 | snprintf_P(svalue, ssvalue, PSTR("{\"IRHVAC\":\"Wrong Vendor, Mode and/or FanSpeed\"}")); 146 | } 147 | } 148 | #endif // USE_IR_HVAC 149 | else { 150 | serviced = false; // Unknown command 151 | } 152 | return serviced; 153 | } 154 | 155 | #ifdef USE_IR_HVAC 156 | boolean ir_hvac_toshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp) 157 | { 158 | unsigned int rawdata[2 + 2*8*HVAC_TOSHIBA_DATALEN + 2]; 159 | byte data[HVAC_TOSHIBA_DATALEN] = { 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 }; 160 | 161 | char *p; 162 | char *token; 163 | uint8_t mode; 164 | 165 | if (HVAC_Mode == NULL) { 166 | p = (char*)HVACMODE; // default HVAC_HOT 167 | } else { 168 | p = strchr(HVACMODE, HVAC_Mode[0]); 169 | } 170 | if (!p) { 171 | return true; 172 | } 173 | data[6] = (p - HVACMODE) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00 174 | 175 | if (!HVAC_Power) { 176 | data[6] = (byte) 0x07; // Turn OFF HVAC 177 | } 178 | 179 | if (HVAC_FanMode == NULL) { 180 | p = (char*)FANSPEED; // default FAN_SPEED_AUTO 181 | } else { 182 | p = strchr(FANSPEED, HVAC_FanMode[0]); 183 | } 184 | if (!p) { 185 | return true; 186 | } 187 | mode = p - FANSPEED +1; 188 | if ((1 == mode) || (7 == mode)) { 189 | mode = 0; 190 | } 191 | mode = mode << 5; // AUTO = 0x00, SPEED = 0x40, 0x60, 0x80, 0xA0, 0xC0, SILENT = 0x00 192 | data[6] = data[6] | mode; 193 | 194 | byte Temp; 195 | if (HVAC_Temp > 30) { 196 | Temp = 30; 197 | } 198 | else if (HVAC_Temp < 17) { 199 | Temp = 17; 200 | } 201 | else { 202 | Temp = HVAC_Temp; 203 | } 204 | data[5] = (byte) Temp - 17 << 4; 205 | 206 | data[HVAC_TOSHIBA_DATALEN-1] = 0; 207 | for (int x = 0; x < HVAC_TOSHIBA_DATALEN - 1; x++) { 208 | data[HVAC_TOSHIBA_DATALEN-1] = (byte) data[x] ^ data[HVAC_TOSHIBA_DATALEN -1]; // CRC is a simple bits addition 209 | } 210 | 211 | int i = 0; 212 | byte mask = 1; 213 | 214 | //header 215 | rawdata[i++] = HVAC_TOSHIBA_HDR_MARK; 216 | rawdata[i++] = HVAC_TOSHIBA_HDR_SPACE; 217 | 218 | //data 219 | for (int b = 0; b < HVAC_TOSHIBA_DATALEN; b++) { 220 | for (mask = B10000000; mask > 0; mask >>= 1) { //iterate through bit mask 221 | if (data[b] & mask) { // Bit ONE 222 | rawdata[i++] = HVAC_TOSHIBA_BIT_MARK; 223 | rawdata[i++] = HVAC_TOSHIBA_ONE_SPACE; 224 | } 225 | else { // Bit ZERO 226 | rawdata[i++] = HVAC_TOSHIBA_BIT_MARK; 227 | rawdata[i++] = HVAC_MISTUBISHI_ZERO_SPACE; 228 | } 229 | } 230 | } 231 | 232 | //trailer 233 | rawdata[i++] = HVAC_TOSHIBA_RPT_MARK; 234 | rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE; 235 | 236 | noInterrupts(); 237 | irsend->sendRaw(rawdata,i,38); 238 | irsend->sendRaw(rawdata,i,38); 239 | interrupts(); 240 | 241 | return false; 242 | } 243 | 244 | boolean ir_hvac_mitsubishi(const char *HVAC_Mode,const char *HVAC_FanMode, boolean HVAC_Power, int HVAC_Temp) 245 | { 246 | char *p; 247 | char *token; 248 | uint8_t mode; 249 | char log[LOGSZ]; 250 | 251 | mitsubir->stateReset(); 252 | 253 | if (HVAC_Mode == NULL) { 254 | p = (char*)HVACMODE; // default HVAC_HOT 255 | } else { 256 | p = strchr(HVACMODE, HVAC_Mode[0]); 257 | } 258 | if (!p) { 259 | return true; 260 | } 261 | mode = (p - HVACMODE +1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20 262 | mitsubir->setMode(mode); 263 | 264 | mitsubir->setPower(~HVAC_Power); 265 | 266 | if (HVAC_FanMode == NULL) { 267 | p = (char*)FANSPEED; // default FAN_SPEED_AUTO 268 | } else { 269 | p = strchr(FANSPEED, HVAC_FanMode[0]); 270 | } 271 | if (!p) { 272 | return true; 273 | } 274 | mode = p - FANSPEED; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6 275 | mitsubir->setFan(mode); 276 | 277 | mitsubir->setTemp(HVAC_Temp); 278 | mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO); 279 | mitsubir->send(); 280 | 281 | // snprintf_P(log, sizeof(log), PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), 282 | // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); 283 | // addLog(LOG_LEVEL_DEBUG, log); 284 | 285 | return false; 286 | } 287 | #endif // USE_IR_HVAC 288 | #endif // USE_IR_REMOTE 289 | -------------------------------------------------------------------------------- /sonoff/xdrv_snfled.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xdrv_snfled.ino - sonoff led support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /*********************************************************************************************\ 21 | * Sonoff Led 22 | \*********************************************************************************************/ 23 | 24 | uint8_t ledTable[] = { 25 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 26 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27 | 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 28 | 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 29 | 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 30 | 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 31 | 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32 | 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 33 | 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 34 | 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 35 | 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99, 36 | 101,102,104,105,107,108,110,111,113,114,116,117,119,121,122,124, 37 | 125,127,129,130,132,134,135,137,139,141,142,144,146,148,150,151, 38 | 153,155,157,159,161,163,165,166,168,170,172,174,176,178,180,182, 39 | 184,186,189,191,193,195,197,199,201,204,206,208,210,212,215,217, 40 | 219,221,224,226,228,231,233,235,238,240,243,245,248,250,253,255 }; 41 | 42 | uint8_t sl_dcolor[2]; 43 | uint8_t sl_tcolor[2]; 44 | uint8_t sl_lcolor[2]; 45 | 46 | uint8_t sl_power; 47 | uint8_t sl_any; 48 | uint8_t sl_wakeupActive = 0; 49 | uint8_t sl_wakeupDimmer = 0; 50 | uint16_t sl_wakeupCntr = 0; 51 | 52 | uint32_t Atoh(char *s) 53 | { 54 | uint32_t value = 0; 55 | uint32_t digit; 56 | int8_t c; 57 | 58 | while((c = *s++)) { 59 | if ('0' <= c && c <= '9') { 60 | digit = c - '0'; 61 | } 62 | else if ('A' <= c && c <= 'F') { 63 | digit = c - 'A' + 10; 64 | } 65 | else if ('a' <= c && c <= 'f') { 66 | digit = c - 'a' + 10; 67 | } 68 | else { 69 | break; 70 | } 71 | value = (value << 4) | digit; 72 | } 73 | return value; 74 | } 75 | 76 | void sl_setDim(uint8_t myDimmer) 77 | { 78 | float newDim = 100 / (float)myDimmer; 79 | float fmyCld = (float)sysCfg.led_color[0] / newDim; 80 | float fmyWrm = (float)sysCfg.led_color[1] / newDim; 81 | sl_dcolor[0] = (uint8_t)fmyCld; 82 | sl_dcolor[1] = (uint8_t)fmyWrm; 83 | } 84 | 85 | /********************************************************************************************/ 86 | 87 | void sl_init(void) 88 | { 89 | sysCfg.pwmvalue[0] = 0; // We use led_color 90 | sysCfg.pwmvalue[1] = 0; // We use led_color 91 | sl_power = 0; 92 | sl_any = 0; 93 | sl_wakeupActive = 0; 94 | } 95 | 96 | void sl_setPower(uint8_t power) 97 | { 98 | sl_power = power &1; 99 | sl_wakeupActive = 0; 100 | sl_animate(); 101 | } 102 | 103 | void sl_animate() 104 | { 105 | // {"Wakeup":"Done"} 106 | char svalue[32]; // was MESSZ 107 | uint8_t fadeValue; 108 | 109 | if (0 == sl_power) { // Power Off 110 | sl_tcolor[0] = 0; 111 | sl_tcolor[1] = 0; 112 | } 113 | else { 114 | if (!sl_wakeupActive) { // Power On 115 | sl_setDim(sysCfg.led_dimmer[0]); 116 | if (0 == sysCfg.led_fade) { 117 | sl_tcolor[0] = sl_dcolor[0]; 118 | sl_tcolor[1] = sl_dcolor[1]; 119 | } else { 120 | if ((sl_tcolor[0] != sl_dcolor[0]) || (sl_tcolor[1] != sl_dcolor[1])) { 121 | if (sl_tcolor[0] < sl_dcolor[0]) { 122 | sl_tcolor[0] += ((sl_dcolor[0] - sl_tcolor[0]) >> sysCfg.led_speed) +1; 123 | } 124 | if (sl_tcolor[1] < sl_dcolor[1]) { 125 | sl_tcolor[1] += ((sl_dcolor[1] - sl_tcolor[1]) >> sysCfg.led_speed) +1; 126 | } 127 | if (sl_tcolor[0] > sl_dcolor[0]) { 128 | sl_tcolor[0] -= ((sl_tcolor[0] - sl_dcolor[0]) >> sysCfg.led_speed) +1; 129 | } 130 | if (sl_tcolor[1] > sl_dcolor[1]) { 131 | sl_tcolor[1] -= ((sl_tcolor[1] - sl_dcolor[1]) >> sysCfg.led_speed) +1; 132 | } 133 | } 134 | } 135 | } else { // Power On using wake up duration 136 | if (1 == sl_wakeupActive) { 137 | sl_wakeupActive = 2; 138 | sl_tcolor[0] = 0; 139 | sl_tcolor[1] = 0; 140 | sl_wakeupCntr = 0; 141 | sl_wakeupDimmer = 0; 142 | } 143 | sl_wakeupCntr++; 144 | if (sl_wakeupCntr > ((sysCfg.led_wakeup * STATES) / sysCfg.led_dimmer[0])) { 145 | sl_wakeupCntr = 0; 146 | sl_wakeupDimmer++; 147 | if (sl_wakeupDimmer <= sysCfg.led_dimmer[0]) { 148 | sl_setDim(sl_wakeupDimmer); 149 | sl_tcolor[0] = sl_dcolor[0]; 150 | sl_tcolor[1] = sl_dcolor[1]; 151 | } else { 152 | snprintf_P(svalue, sizeof(svalue), PSTR("{\"Wakeup\":\"Done\"}")); 153 | mqtt_publish_topic_P(2, PSTR("WAKEUP"), svalue); 154 | sl_wakeupActive = 0; 155 | } 156 | } 157 | } 158 | } 159 | if ((sl_lcolor[0] != sl_tcolor[0]) || (sl_lcolor[1] != sl_tcolor[1]) || sl_any) { 160 | sl_any = 0; 161 | sl_lcolor[0] = sl_tcolor[0]; 162 | sl_lcolor[1] = sl_tcolor[1]; 163 | for (byte i = 0; i < 2; i++) { 164 | if (pin[GPIO_PWM1 +i] < 99) { 165 | analogWrite(pin[GPIO_PWM1 +i], ((sysCfg.led_table) ? ledTable[sl_lcolor[i]] : sl_lcolor[i]) * (PWM_RANGE / 255)); 166 | } 167 | } 168 | } 169 | } 170 | 171 | /*********************************************************************************************\ 172 | * Commands 173 | \*********************************************************************************************/ 174 | 175 | boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue) 176 | { 177 | boolean serviced = true; 178 | boolean coldim = false; 179 | 180 | if (!strcmp_P(type,PSTR("COLOR"))) { 181 | uint8_t my_color[5]; 182 | if (4 == data_len) { 183 | char ccold[3], cwarm[3]; 184 | memcpy(ccold, dataBufUc, 2); 185 | ccold[2] = '\0'; 186 | memcpy(cwarm, dataBufUc + 2, 2); 187 | cwarm[2] = '\0'; 188 | my_color[0] = Atoh(ccold); 189 | my_color[1] = Atoh(cwarm); 190 | uint16_t temp = my_color[0]; 191 | if (temp < my_color[1]) { 192 | temp = my_color[1]; 193 | } 194 | float mDim = (float)temp / 2.55; 195 | sysCfg.led_dimmer[0] = (uint8_t)mDim; 196 | float newDim = 100 / mDim; 197 | float fmyCold = (float)my_color[0] * newDim; 198 | float fmyWarm = (float)my_color[1] * newDim; 199 | sysCfg.led_color[0] = (uint8_t)fmyCold; 200 | sysCfg.led_color[1] = (uint8_t)fmyWarm; 201 | coldim = true; 202 | } else { 203 | sl_setDim(sysCfg.led_dimmer[0]); 204 | uint16_t color = (uint16_t)sl_dcolor[0] << 8; 205 | color += (uint16_t)sl_dcolor[1]; 206 | snprintf_P(svalue, ssvalue, PSTR("{\"Color\":\"%04X\"}"), color); 207 | } 208 | } 209 | else if (!strcmp_P(type,PSTR("DIMMER"))) { 210 | if ((data_len > 0) && (payload >= 0) && (payload <= 100)) { 211 | sysCfg.led_dimmer[0] = payload; 212 | coldim = true; 213 | } else { 214 | snprintf_P(svalue, ssvalue, PSTR("{\"Dimmer\":%d}"), sysCfg.led_dimmer[0]); 215 | } 216 | } 217 | else if (!strcmp_P(type,PSTR("LEDTABLE"))) { 218 | if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { 219 | switch (payload) { 220 | case 0: // Off 221 | case 1: // On 222 | sysCfg.led_table = payload; 223 | break; 224 | case 2: // Toggle 225 | sysCfg.led_table ^= 1; 226 | break; 227 | } 228 | sl_any = 1; 229 | } 230 | snprintf_P(svalue, ssvalue, PSTR("{\"LedTable\":\"%s\"}"), getStateText(sysCfg.led_table)); 231 | } 232 | else if (!strcmp_P(type,PSTR("FADE"))) { 233 | if ((data_len > 0) && (payload >= 0) && (payload <= 2)) { 234 | switch (payload) { 235 | case 0: // Off 236 | case 1: // On 237 | sysCfg.led_fade = payload; 238 | break; 239 | case 2: // Toggle 240 | sysCfg.led_fade ^= 1; 241 | break; 242 | } 243 | } 244 | snprintf_P(svalue, ssvalue, PSTR("{\"Fade\":\"%s\"}"), getStateText(sysCfg.led_fade)); 245 | } 246 | else if (!strcmp_P(type,PSTR("SPEED"))) { // 1 - fast, 8 - slow 247 | if ((data_len > 0) && (payload > 0) && (payload <= 8)) { 248 | sysCfg.led_speed = payload; 249 | } 250 | snprintf_P(svalue, ssvalue, PSTR("{\"Speed\":%d}"), sysCfg.led_speed); 251 | } 252 | else if (!strcmp_P(type,PSTR("WAKEUPDURATION"))) { 253 | if ((data_len > 0) && (payload > 0) && (payload < 3601)) { 254 | sysCfg.led_wakeup = payload; 255 | sl_wakeupActive = 0; 256 | } 257 | snprintf_P(svalue, ssvalue, PSTR("{\"WakeUpDuration\":%d}"), sysCfg.led_wakeup); 258 | } 259 | else if (!strcmp_P(type,PSTR("WAKEUP"))) { 260 | do_cmnd_power(index, 1); 261 | sl_wakeupActive = 1; 262 | snprintf_P(svalue, ssvalue, PSTR("{\"Wakeup\":\"Started\"}")); 263 | } 264 | else { 265 | serviced = false; // Unknown command 266 | } 267 | if (coldim) { 268 | // do_cmnd_power(index, (sysCfg.led_dimmer[0]>0)); 269 | if (sysCfg.led_dimmer[0] && !(power&1)) { 270 | do_cmnd_power(1, 1); 271 | } 272 | else if (!sysCfg.led_dimmer[0] && (power&1)) { 273 | do_cmnd_power(1, 0); 274 | } 275 | #ifdef USE_DOMOTICZ 276 | mqtt_publishDomoticzPowerState(1); 277 | #endif // USE_DOMOTICZ 278 | sl_setDim(sysCfg.led_dimmer[0]); 279 | uint16_t color = (uint16_t)sl_dcolor[0] << 8; 280 | color += (uint16_t)sl_dcolor[1]; 281 | snprintf_P(svalue, ssvalue, PSTR("{\"POWER\":\"%s\", \"Dimmer\":%d, \"Color\":\"%04X\"}"), 282 | getStateText(power &1), sysCfg.led_dimmer[0], color); 283 | } 284 | return serviced; 285 | } 286 | 287 | -------------------------------------------------------------------------------- /sonoff/xdrv_snfsc.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xdrv_snfsc.ino - sonoff SC support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Heiko Krupp and Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | /*********************************************************************************************\ 21 | Sonoff Sc 22 | 23 | sc_value[0] DHT11 Humidity 24 | sc_value[1] DHT11 Temperature in Celsius 25 | sc_value[2] Light level from 1 (Dark) to 10 (Bright) - inverted from original 26 | sc_value[3] Noise level from 1 (Quiet) to 10 (Loud) 27 | sc_value[4] Air Quality level from 1 (Bad) to 10 (Good) - inverted from original 28 | 29 | To ATMEGA328P: 30 | AT+DEVCONFIG="uploadFreq":1800,"humiThreshold":2,"tempThreshold":1[1B] 31 | AT+NOTIFY="uploadFreq":1800,"humiThreshold":2,"tempThreshold":1[1B] 32 | response: AT+NOTIFY=ok[1B] 33 | AT+SEND=fail[1B] 34 | AT+SEND=ok[1B] 35 | AT+STATUS=4[1B] 36 | AT+STATUS[1B] 37 | AT+START[1B] 38 | 39 | From ATMEGA328P: 40 | AT+UPDATE="humidity":42,"temperature":20,"light":7,"noise":3,"dusty":1[1B] 41 | response: AT+SEND=ok[1B] or AT+SEND=fail[1B] 42 | AT+STATUS?[1B] 43 | response: AT+STATUS=4[1B] 44 | 45 | Sequence: 46 | SC sends: ATMEGA328P sends: 47 | AT+START[1B] 48 | AT+UPDATE="humidity":42,"temperature":20,"light":7,"noise":3,"dusty":1[1B] 49 | AT+SEND=ok[1B] 50 | 51 | AT+STATUS?[1B] 52 | AT+STATUS=4[1B] 53 | 54 | \*********************************************************************************************/ 55 | 56 | uint16_t sc_value[5] = { 0 }; 57 | 58 | void sc_init() 59 | { 60 | // Serial.write("AT+DEVCONFIG=\"uploadFreq\":1800\e"); 61 | Serial.write("AT+START\e"); 62 | // Serial.write("AT+STATUS\e"); 63 | } 64 | 65 | void sc_rcvstat(char *rcvstat) 66 | { 67 | char *p; 68 | char *str; 69 | uint16_t value[5] = { 0 }; 70 | 71 | if (!strncmp(rcvstat, "AT+UPDATE=", 10)) { 72 | int8_t i = -1; 73 | for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(NULL, ":", &p)) { 74 | value[i++] = atoi(str); 75 | } 76 | if (value[0] > 0) { 77 | for (byte i = 0; i < 5; i++) { 78 | sc_value[i] = value[i]; 79 | } 80 | sc_value[2] = (11 - sc_value[2]) * 10; // Invert light level 81 | sc_value[3] *= 10; 82 | sc_value[4] = (11 - sc_value[4]) * 10; // Invert dust level 83 | Serial.write("AT+SEND=ok\e"); 84 | } else { 85 | Serial.write("AT+SEND=fail\e"); 86 | } 87 | } 88 | else if (!strcmp_P(rcvstat,PSTR("AT+STATUS?"))) { 89 | Serial.write("AT+STATUS=4\e"); 90 | } 91 | } 92 | 93 | /*********************************************************************************************\ 94 | * Presentation 95 | \*********************************************************************************************/ 96 | 97 | float sc_convertCtoF(float c) 98 | { 99 | return c * 1.8 + 32; 100 | } 101 | 102 | void sc_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 103 | { 104 | if (sc_value[0] > 0) { 105 | char stemp1[10]; 106 | char stemp2[10]; 107 | 108 | float t = convertTemp(sc_value[1]); 109 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp1); 110 | float h = sc_value[0]; 111 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, stemp2); 112 | // snprintf_P(svalue, ssvalue, PSTR("%s, \"SC\":{\"Temperature\":%s, \"Humidity\":%s, \"Light\":%d, \"Noise\":%d, \"AirQuality\":%d}"), 113 | snprintf_P(svalue, ssvalue, PSTR("%s, \"Temperature\":%s, \"Humidity\":%s, \"Light\":%d, \"Noise\":%d, \"AirQuality\":%d"), 114 | svalue, stemp1, stemp2, sc_value[2], sc_value[3], sc_value[4]); 115 | *djson = 1; 116 | #ifdef USE_DOMOTICZ 117 | domoticz_sensor2(stemp1, stemp2); 118 | domoticz_sensor5(sc_value[2]); 119 | #endif // USE_DOMOTICZ 120 | } 121 | } 122 | 123 | #ifdef USE_WEBSERVER 124 | String sc_webPresent() 125 | { 126 | String page = ""; 127 | 128 | if (sc_value[0] > 0) { 129 | char stemp[10]; 130 | char sensor[80]; 131 | char scstype[] = ""; 132 | 133 | float t = convertTemp(sc_value[1]); 134 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp); 135 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, scstype, stemp, tempUnit()); 136 | page += sensor; 137 | float h = sc_value[0]; 138 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, stemp); 139 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_HUM, scstype, stemp); 140 | page += sensor; 141 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_LIGHT, scstype, sc_value[2]); 142 | page += sensor; 143 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_NOISE, scstype, sc_value[3]); 144 | page += sensor; 145 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_DUST, scstype, sc_value[4]); 146 | page += sensor; 147 | } 148 | return page; 149 | } 150 | #endif // USE_WEBSERVER 151 | 152 | -------------------------------------------------------------------------------- /sonoff/xsns_bh1750.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_bh1750.ino - BH1750 ambient light sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_I2C 21 | #ifdef USE_BH1750 22 | /*********************************************************************************************\ 23 | * BH1750 - Ambient Light Intensity 24 | \*********************************************************************************************/ 25 | 26 | #define BH1750_ADDR1 0x23 27 | #define BH1750_ADDR2 0x5C 28 | 29 | #define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1lx resolution. Measurement time is approx 120ms. 30 | 31 | uint8_t bh1750addr; 32 | uint8_t bh1750type = 0; 33 | char bh1750stype[7]; 34 | 35 | uint16_t bh1750_readLux(void) 36 | { 37 | Wire.requestFrom(bh1750addr, (uint8_t)2); 38 | byte msb = Wire.read(); 39 | byte lsb = Wire.read(); 40 | uint16_t value = ((msb << 8) | lsb) / 1.2; 41 | return value; 42 | } 43 | 44 | boolean bh1750_detect() 45 | { 46 | if (bh1750type) { 47 | return true; 48 | } 49 | 50 | char log[LOGSZ]; 51 | uint8_t status; 52 | boolean success = false; 53 | 54 | bh1750addr = BH1750_ADDR1; 55 | Wire.beginTransmission(bh1750addr); 56 | Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); 57 | status = Wire.endTransmission(); 58 | if (status) { 59 | bh1750addr = BH1750_ADDR2; 60 | Wire.beginTransmission(bh1750addr); 61 | Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); 62 | status = Wire.endTransmission(); 63 | } 64 | if (!status) { 65 | success = true; 66 | bh1750type = 1; 67 | strcpy(bh1750stype, "BH1750"); 68 | } 69 | if (success) { 70 | snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), bh1750stype, bh1750addr); 71 | addLog(LOG_LEVEL_DEBUG, log); 72 | } else { 73 | bh1750type = 0; 74 | } 75 | return success; 76 | } 77 | 78 | /*********************************************************************************************\ 79 | * Presentation 80 | \*********************************************************************************************/ 81 | 82 | void bh1750_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 83 | { 84 | if (!bh1750type) { 85 | return; 86 | } 87 | 88 | uint16_t l = bh1750_readLux(); 89 | snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Illuminance\":%d}"), svalue, bh1750stype, l); 90 | *djson = 1; 91 | #ifdef USE_DOMOTICZ 92 | domoticz_sensor5(l); 93 | #endif // USE_DOMOTICZ 94 | } 95 | 96 | #ifdef USE_WEBSERVER 97 | const char HTTP_SNS_ILLUMINANCE[] PROGMEM = 98 | "BH1750 Illuminance%d lx"; 99 | 100 | String bh1750_webPresent() 101 | { 102 | String page = ""; 103 | if (bh1750type) { 104 | char sensor[80]; 105 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_ILLUMINANCE, bh1750_readLux()); 106 | page += sensor; 107 | } 108 | return page; 109 | } 110 | #endif // USE_WEBSERVER 111 | #endif // USE_BH1750 112 | #endif // USE_I2C 113 | 114 | -------------------------------------------------------------------------------- /sonoff/xsns_bmp.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_bmp.ino - BMP pressure, temperature and humidity sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Heiko Krupp and Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_I2C 21 | #ifdef USE_BMP 22 | /*********************************************************************************************\ 23 | * BMP085, BMP180, BMP280, BME280 - Pressure and Temperature and Humidy (BME280 only) 24 | * 25 | * Source: Heiko Krupp and Adafruit Industries 26 | \*********************************************************************************************/ 27 | 28 | #define BMP_ADDR 0x77 29 | 30 | #define BMP180_CHIPID 0x55 31 | #define BMP280_CHIPID 0x58 32 | #define BME280_CHIPID 0x60 33 | 34 | #define BMP_REGISTER_CHIPID 0xD0 35 | 36 | uint8_t bmpaddr; 37 | uint8_t bmptype = 0; 38 | char bmpstype[7]; 39 | 40 | /*********************************************************************************************\ 41 | * BMP085 and BME180 42 | * 43 | * Programmer : Heiko Krupp with changes from Theo Arends 44 | \*********************************************************************************************/ 45 | 46 | #define BMP180_REG_CONTROL 0xF4 47 | #define BMP180_REG_RESULT 0xF6 48 | #define BMP180_TEMPERATURE 0x2E 49 | #define BMP180_PRESSURE3 0xF4 // Max. oversampling -> OSS = 3 50 | 51 | #define BMP180_AC1 0xAA 52 | #define BMP180_AC2 0xAC 53 | #define BMP180_AC3 0xAE 54 | #define BMP180_AC4 0xB0 55 | #define BMP180_AC5 0xB2 56 | #define BMP180_AC6 0xB4 57 | #define BMP180_VB1 0xB6 58 | #define BMP180_VB2 0xB8 59 | #define BMP180_MB 0xBA 60 | #define BMP180_MC 0xBC 61 | #define BMP180_MD 0xBE 62 | 63 | #define BMP180_OSS 3 64 | 65 | int16_t cal_ac1; 66 | int16_t cal_ac2; 67 | int16_t cal_ac3; 68 | int16_t cal_b1; 69 | int16_t cal_b2; 70 | int16_t cal_mc; 71 | int16_t cal_md; 72 | uint16_t cal_ac4; 73 | uint16_t cal_ac5; 74 | uint16_t cal_ac6; 75 | int32_t bmp180_b5 = 0; 76 | 77 | boolean bmp180_calibration() 78 | { 79 | cal_ac1 = i2c_read16(bmpaddr, BMP180_AC1); 80 | cal_ac2 = i2c_read16(bmpaddr, BMP180_AC2); 81 | cal_ac3 = i2c_read16(bmpaddr, BMP180_AC3); 82 | cal_ac4 = i2c_read16(bmpaddr, BMP180_AC4); 83 | cal_ac5 = i2c_read16(bmpaddr, BMP180_AC5); 84 | cal_ac6 = i2c_read16(bmpaddr, BMP180_AC6); 85 | cal_b1 = i2c_read16(bmpaddr, BMP180_VB1); 86 | cal_b2 = i2c_read16(bmpaddr, BMP180_VB2); 87 | cal_mc = i2c_read16(bmpaddr, BMP180_MC); 88 | cal_md = i2c_read16(bmpaddr, BMP180_MD); 89 | 90 | // Check for Errors in calibration data. Value never is 0x0000 or 0xFFFF 91 | if (!cal_ac1 | !cal_ac2 | !cal_ac3 | !cal_ac4 | !cal_ac5 | !cal_ac6 | !cal_b1 | !cal_b2 | !cal_mc | !cal_md) { 92 | return false; 93 | } 94 | 95 | if ((cal_ac1 == 0xFFFF)| 96 | (cal_ac2 == 0xFFFF)| 97 | (cal_ac3 == 0xFFFF)| 98 | (cal_ac4 == 0xFFFF)| 99 | (cal_ac5 == 0xFFFF)| 100 | (cal_ac6 == 0xFFFF)| 101 | (cal_b1 == 0xFFFF)| 102 | (cal_b2 == 0xFFFF)| 103 | (cal_mc == 0xFFFF)| 104 | (cal_md == 0xFFFF)) { 105 | return false; 106 | } 107 | 108 | return true; 109 | } 110 | 111 | double bmp180_readTemperature() 112 | { 113 | i2c_write8(bmpaddr, BMP180_REG_CONTROL, BMP180_TEMPERATURE); 114 | delay(5); // 5ms conversion time 115 | int ut = i2c_read16(bmpaddr, BMP180_REG_RESULT); 116 | int32_t x1 = (ut - (int32_t)cal_ac6) * ((int32_t)cal_ac5) >> 15; 117 | int32_t x2 = ((int32_t)cal_mc << 11) / (x1+(int32_t)cal_md); 118 | bmp180_b5=x1+x2; 119 | 120 | return ((bmp180_b5+8)>>4)/10.0; 121 | } 122 | 123 | double bmp180_readPressure() 124 | { 125 | int32_t p; 126 | uint8_t msb; 127 | uint8_t lsb; 128 | uint8_t xlsb; 129 | 130 | i2c_write8(bmpaddr, BMP180_REG_CONTROL, BMP180_PRESSURE3); // Highest resolution 131 | delay(2 + (4 << BMP180_OSS)); // 26ms conversion time at ultra high resolution 132 | uint32_t up = i2c_read24(bmpaddr, BMP180_REG_RESULT); 133 | up >>= (8 - BMP180_OSS); 134 | 135 | int32_t b6 = bmp180_b5 - 4000; 136 | int32_t x1 = ((int32_t)cal_b2 * ( (b6 * b6)>>12 )) >> 11; 137 | int32_t x2 = ((int32_t)cal_ac2 * b6) >> 11; 138 | int32_t x3 = x1 + x2; 139 | int32_t b3 = ((((int32_t)cal_ac1*4 + x3) << BMP180_OSS) + 2)>>2; 140 | 141 | x1 = ((int32_t)cal_ac3 * b6) >> 13; 142 | x2 = ((int32_t)cal_b1 * ((b6 * b6) >> 12)) >> 16; 143 | x3 = ((x1 + x2) + 2) >> 2; 144 | uint32_t b4 = ((uint32_t)cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; 145 | uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)( 50000UL >> BMP180_OSS); 146 | 147 | if (b7 < 0x80000000) { 148 | p = (b7 * 2) / b4; 149 | } else { 150 | p = (b7 / b4) * 2; 151 | } 152 | 153 | x1 = (p >> 8) * (p >> 8); 154 | x1 = (x1 * 3038) >> 16; 155 | x2 = (-7357 * p) >> 16; 156 | 157 | p += ((x1 + x2 + (int32_t)3791)>>4); 158 | return p/100.0; // convert to mbar 159 | } 160 | 161 | double bmp180_calcSealevelPressure(float pAbs, float altitude_meters) 162 | { 163 | double pressure = pAbs*100.0; 164 | return (double)(pressure / pow(1.0-altitude_meters/44330, 5.255))/100.0; 165 | } 166 | 167 | /*********************************************************************************************\ 168 | * BMP280 and BME280 169 | * 170 | * Programmer : BMP280/BME280 Datasheet and Adafruit with changes by Theo Arends 171 | \*********************************************************************************************/ 172 | 173 | #define BME280_REGISTER_CONTROLHUMID 0xF2 174 | #define BME280_REGISTER_CONTROL 0xF4 175 | #define BME280_REGISTER_PRESSUREDATA 0xF7 176 | #define BME280_REGISTER_TEMPDATA 0xFA 177 | #define BME280_REGISTER_HUMIDDATA 0xFD 178 | 179 | #define BME280_REGISTER_DIG_T1 0x88 180 | #define BME280_REGISTER_DIG_T2 0x8A 181 | #define BME280_REGISTER_DIG_T3 0x8C 182 | #define BME280_REGISTER_DIG_P1 0x8E 183 | #define BME280_REGISTER_DIG_P2 0x90 184 | #define BME280_REGISTER_DIG_P3 0x92 185 | #define BME280_REGISTER_DIG_P4 0x94 186 | #define BME280_REGISTER_DIG_P5 0x96 187 | #define BME280_REGISTER_DIG_P6 0x98 188 | #define BME280_REGISTER_DIG_P7 0x9A 189 | #define BME280_REGISTER_DIG_P8 0x9C 190 | #define BME280_REGISTER_DIG_P9 0x9E 191 | #define BME280_REGISTER_DIG_H1 0xA1 192 | #define BME280_REGISTER_DIG_H2 0xE1 193 | #define BME280_REGISTER_DIG_H3 0xE3 194 | #define BME280_REGISTER_DIG_H4 0xE4 195 | #define BME280_REGISTER_DIG_H5 0xE5 196 | #define BME280_REGISTER_DIG_H6 0xE7 197 | 198 | struct bme280_calib_data 199 | { 200 | uint16_t dig_T1; 201 | int16_t dig_T2; 202 | int16_t dig_T3; 203 | uint16_t dig_P1; 204 | int16_t dig_P2; 205 | int16_t dig_P3; 206 | int16_t dig_P4; 207 | int16_t dig_P5; 208 | int16_t dig_P6; 209 | int16_t dig_P7; 210 | int16_t dig_P8; 211 | int16_t dig_P9; 212 | uint8_t dig_H1; 213 | int16_t dig_H2; 214 | uint8_t dig_H3; 215 | int16_t dig_H4; 216 | int16_t dig_H5; 217 | int8_t dig_H6; 218 | } _bme280_calib; 219 | 220 | int32_t t_fine; 221 | 222 | boolean bmp280_calibrate() 223 | { 224 | // if (i2c_read8(bmpaddr, BMP_REGISTER_CHIPID) != BMP280_CHIPID) return false; 225 | 226 | _bme280_calib.dig_T1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_T1); 227 | _bme280_calib.dig_T2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T2); 228 | _bme280_calib.dig_T3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T3); 229 | _bme280_calib.dig_P1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_P1); 230 | _bme280_calib.dig_P2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P2); 231 | _bme280_calib.dig_P3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P3); 232 | _bme280_calib.dig_P4 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P4); 233 | _bme280_calib.dig_P5 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P5); 234 | _bme280_calib.dig_P6 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P6); 235 | _bme280_calib.dig_P7 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P7); 236 | _bme280_calib.dig_P8 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P8); 237 | _bme280_calib.dig_P9 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P9); 238 | 239 | // i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0x3F); // Temp 1x oversampling, Press 16x oversampling, normal mode (Adafruit) 240 | i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit) 241 | 242 | return true; 243 | } 244 | 245 | boolean bme280_calibrate() 246 | { 247 | // if (i2c_read8(bmpaddr, BMP_REGISTER_CHIPID) != BME280_CHIPID) return false; 248 | 249 | _bme280_calib.dig_T1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_T1); 250 | _bme280_calib.dig_T2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T2); 251 | _bme280_calib.dig_T3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T3); 252 | _bme280_calib.dig_P1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_P1); 253 | _bme280_calib.dig_P2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P2); 254 | _bme280_calib.dig_P3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P3); 255 | _bme280_calib.dig_P4 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P4); 256 | _bme280_calib.dig_P5 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P5); 257 | _bme280_calib.dig_P6 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P6); 258 | _bme280_calib.dig_P7 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P7); 259 | _bme280_calib.dig_P8 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P8); 260 | _bme280_calib.dig_P9 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P9); 261 | _bme280_calib.dig_H1 = i2c_read8(bmpaddr, BME280_REGISTER_DIG_H1); 262 | _bme280_calib.dig_H2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_H2); 263 | _bme280_calib.dig_H3 = i2c_read8(bmpaddr, BME280_REGISTER_DIG_H3); 264 | _bme280_calib.dig_H4 = (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H4) << 4) | (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H4 + 1) & 0xF); 265 | _bme280_calib.dig_H5 = (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H5 + 1) << 4) | (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H5) >> 4); 266 | _bme280_calib.dig_H6 = (int8_t)i2c_read8(bmpaddr, BME280_REGISTER_DIG_H6); 267 | 268 | // Set before CONTROL_meas (DS 5.4.3) 269 | i2c_write8(bmpaddr, BME280_REGISTER_CONTROLHUMID, 0x05); // 16x oversampling (Adafruit) 270 | i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit) 271 | 272 | return true; 273 | } 274 | 275 | double bmp280_readTemperature(void) 276 | { 277 | int32_t var1; 278 | int32_t var2; 279 | 280 | int32_t adc_T = i2c_read24(bmpaddr, BME280_REGISTER_TEMPDATA); 281 | adc_T >>= 4; 282 | 283 | var1 = ((((adc_T>>3) - ((int32_t)_bme280_calib.dig_T1 <<1))) * ((int32_t)_bme280_calib.dig_T2)) >> 11; 284 | var2 = (((((adc_T>>4) - ((int32_t)_bme280_calib.dig_T1)) * ((adc_T>>4) - ((int32_t)_bme280_calib.dig_T1))) >> 12) * 285 | ((int32_t)_bme280_calib.dig_T3)) >> 14; 286 | t_fine = var1 + var2; 287 | double T = (t_fine * 5 + 128) >> 8; 288 | return T / 100.0; 289 | } 290 | 291 | double bmp280_readPressure(void) 292 | { 293 | int64_t var1; 294 | int64_t var2; 295 | int64_t p; 296 | 297 | // Must be done first to get the t_fine variable set up 298 | // bmp280_readTemperature(); 299 | 300 | int32_t adc_P = i2c_read24(bmpaddr, BME280_REGISTER_PRESSUREDATA); 301 | adc_P >>= 4; 302 | 303 | var1 = ((int64_t)t_fine) - 128000; 304 | var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6; 305 | var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) << 17); 306 | var2 = var2 + (((int64_t)_bme280_calib.dig_P4) << 35); 307 | var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) >> 8) + ((var1 * (int64_t)_bme280_calib.dig_P2) << 12); 308 | var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)_bme280_calib.dig_P1) >> 33; 309 | if (0 == var1) { 310 | return 0; // avoid exception caused by division by zero 311 | } 312 | p = 1048576 - adc_P; 313 | p = (((p << 31) - var2) * 3125) / var1; 314 | var1 = (((int64_t)_bme280_calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25; 315 | var2 = (((int64_t)_bme280_calib.dig_P8) * p) >> 19; 316 | p = ((p + var1 + var2) >> 8) + (((int64_t)_bme280_calib.dig_P7) << 4); 317 | return (double)p / 25600.0; 318 | } 319 | 320 | double bme280_readHumidity(void) 321 | { 322 | int32_t v_x1_u32r; 323 | 324 | // Must be done first to get the t_fine variable set up 325 | // bmp280_readTemperature(); 326 | 327 | int32_t adc_H = i2c_read16(bmpaddr, BME280_REGISTER_HUMIDDATA); 328 | 329 | v_x1_u32r = (t_fine - ((int32_t)76800)); 330 | 331 | v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bme280_calib.dig_H4) << 20) - 332 | (((int32_t)_bme280_calib.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * 333 | (((((((v_x1_u32r * ((int32_t)_bme280_calib.dig_H6)) >> 10) * 334 | (((v_x1_u32r * ((int32_t)_bme280_calib.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + 335 | ((int32_t)2097152)) * ((int32_t)_bme280_calib.dig_H2) + 8192) >> 14)); 336 | v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * 337 | ((int32_t)_bme280_calib.dig_H1)) >> 4)); 338 | v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; 339 | v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; 340 | double h = (v_x1_u32r >> 12); 341 | return h / 1024.0; 342 | } 343 | 344 | /*********************************************************************************************\ 345 | * BMP 346 | \*********************************************************************************************/ 347 | 348 | double bmp_readTemperature(void) 349 | { 350 | double t = NAN; 351 | 352 | switch (bmptype) { 353 | case BMP180_CHIPID: 354 | t = bmp180_readTemperature(); 355 | break; 356 | case BMP280_CHIPID: 357 | case BME280_CHIPID: 358 | t = bmp280_readTemperature(); 359 | } 360 | if (!isnan(t)) { 361 | t = convertTemp(t); 362 | return t; 363 | } 364 | return 0; 365 | } 366 | 367 | double bmp_readPressure(void) 368 | { 369 | switch (bmptype) { 370 | case BMP180_CHIPID: 371 | return bmp180_readPressure(); 372 | case BMP280_CHIPID: 373 | case BME280_CHIPID: 374 | return bmp280_readPressure(); 375 | } 376 | return 0; 377 | } 378 | 379 | double bmp_readHumidity(void) 380 | { 381 | switch (bmptype) { 382 | case BMP180_CHIPID: 383 | case BMP280_CHIPID: 384 | break; 385 | case BME280_CHIPID: 386 | return bme280_readHumidity(); 387 | } 388 | return 0; 389 | } 390 | 391 | boolean bmp_detect() 392 | { 393 | if (bmptype) { 394 | return true; 395 | } 396 | 397 | char log[LOGSZ]; 398 | boolean success = false; 399 | 400 | bmpaddr = BMP_ADDR; 401 | bmptype = i2c_read8(bmpaddr, BMP_REGISTER_CHIPID); 402 | if (!bmptype) { 403 | bmpaddr--; 404 | bmptype = i2c_read8(bmpaddr, BMP_REGISTER_CHIPID); 405 | } 406 | strcpy_P(bmpstype, PSTR("BMP")); 407 | switch (bmptype) { 408 | case BMP180_CHIPID: 409 | success = bmp180_calibration(); 410 | strcpy_P(bmpstype, PSTR("BMP180")); 411 | break; 412 | case BMP280_CHIPID: 413 | success = bmp280_calibrate(); 414 | strcpy_P(bmpstype, PSTR("BMP280")); 415 | break; 416 | case BME280_CHIPID: 417 | success = bme280_calibrate(); 418 | strcpy_P(bmpstype, PSTR("BME280")); 419 | } 420 | if (success) { 421 | snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), bmpstype, bmpaddr); 422 | addLog(LOG_LEVEL_DEBUG, log); 423 | } else { 424 | bmptype = 0; 425 | } 426 | return success; 427 | } 428 | 429 | /*********************************************************************************************\ 430 | * Presentation 431 | \*********************************************************************************************/ 432 | 433 | void bmp_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 434 | { 435 | if (!bmptype) { 436 | return; 437 | } 438 | 439 | char stemp1[10]; 440 | char stemp2[10]; 441 | char stemp3[10]; 442 | 443 | double t = bmp_readTemperature(); 444 | double p = bmp_readPressure(); 445 | double h = bmp_readHumidity(); 446 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp1); 447 | dtostrf(p, 1, sysCfg.flag.pressure_resolution, stemp2); 448 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, stemp3); 449 | if (!strcmp(bmpstype,"BME280")) { 450 | snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":%s, \"Humidity\":%s, \"Pressure\":%s}"), 451 | svalue, bmpstype, stemp1, stemp3, stemp2); 452 | } else { 453 | snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":%s, \"Pressure\":%s}"), 454 | svalue, bmpstype, stemp1, stemp2); 455 | } 456 | *djson = 1; 457 | #ifdef USE_DOMOTICZ 458 | domoticz_sensor3(stemp1, stemp3, stemp2); 459 | #endif // USE_DOMOTICZ 460 | } 461 | 462 | #ifdef USE_WEBSERVER 463 | String bmp_webPresent() 464 | { 465 | String page = ""; 466 | if (bmptype) { 467 | char stemp[10]; 468 | char sensor[80]; 469 | 470 | double t_bmp = bmp_readTemperature(); 471 | double p_bmp = bmp_readPressure(); 472 | double h_bmp = bmp_readHumidity(); 473 | dtostrf(t_bmp, 1, sysCfg.flag.temperature_resolution, stemp); 474 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, bmpstype, stemp, tempUnit()); 475 | page += sensor; 476 | if (!strcmp(bmpstype,"BME280")) { 477 | dtostrf(h_bmp, 1, sysCfg.flag.humidity_resolution, stemp); 478 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_HUM, bmpstype, stemp); 479 | page += sensor; 480 | } 481 | dtostrf(p_bmp, 1, sysCfg.flag.pressure_resolution, stemp); 482 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_PRESSURE, bmpstype, stemp); 483 | page += sensor; 484 | } 485 | return page; 486 | } 487 | #endif // USE_WEBSERVER 488 | #endif // USE_BMP 489 | #endif // USE_I2C 490 | 491 | -------------------------------------------------------------------------------- /sonoff/xsns_dht.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_dht.ino - DHTxx and AM23xx temperature and humidity sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_DHT 21 | /*********************************************************************************************\ 22 | * DHT11, DHT21 (AM2301), DHT22 (AM2302, AM2321) - Temperature and Humidy 23 | * 24 | * Reading temperature or humidity takes about 250 milliseconds! 25 | * Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) 26 | * Source: Adafruit Industries https://github.com/adafruit/DHT-sensor-library 27 | \*********************************************************************************************/ 28 | 29 | #define DHT_MAX_SENSORS 3 30 | #define MIN_INTERVAL 2000 31 | 32 | uint32_t dht_maxcycles; 33 | uint8_t dht_data[5]; 34 | byte dht_sensors = 0; 35 | 36 | struct DHTSTRUCT { 37 | byte pin; 38 | byte type; 39 | char stype[10]; 40 | uint32_t lastreadtime; 41 | bool lastresult; 42 | float t; 43 | float h = 0; 44 | } dht[DHT_MAX_SENSORS]; 45 | 46 | void dht_readPrep() 47 | { 48 | for (byte i = 0; i < dht_sensors; i++) { 49 | digitalWrite(dht[i].pin, HIGH); 50 | } 51 | } 52 | 53 | uint32_t dht_expectPulse(byte sensor, bool level) 54 | { 55 | uint32_t count = 0; 56 | 57 | while (digitalRead(dht[sensor].pin) == level) { 58 | if (count++ >= dht_maxcycles) { 59 | return 0; 60 | } 61 | } 62 | return count; 63 | } 64 | 65 | boolean dht_read(byte sensor) 66 | { 67 | char log[LOGSZ]; 68 | uint32_t cycles[80]; 69 | uint32_t currenttime = millis(); 70 | 71 | if ((currenttime - dht[sensor].lastreadtime) < 2000) { 72 | return dht[sensor].lastresult; 73 | } 74 | dht[sensor].lastreadtime = currenttime; 75 | 76 | dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; 77 | 78 | // digitalWrite(dht[sensor].pin, HIGH); 79 | // delay(250); 80 | 81 | pinMode(dht[sensor].pin, OUTPUT); 82 | digitalWrite(dht[sensor].pin, LOW); 83 | delay(20); 84 | 85 | noInterrupts(); 86 | digitalWrite(dht[sensor].pin, HIGH); 87 | delayMicroseconds(40); 88 | pinMode(dht[sensor].pin, INPUT_PULLUP); 89 | delayMicroseconds(10); 90 | if (0 == dht_expectPulse(sensor, LOW)) { 91 | addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for start signal low pulse")); 92 | dht[sensor].lastresult = false; 93 | return dht[sensor].lastresult; 94 | } 95 | if (0 == dht_expectPulse(sensor, HIGH)) { 96 | addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for start signal high pulse")); 97 | dht[sensor].lastresult = false; 98 | return dht[sensor].lastresult; 99 | } 100 | for (int i = 0; i < 80; i += 2) { 101 | cycles[i] = dht_expectPulse(sensor, LOW); 102 | cycles[i+1] = dht_expectPulse(sensor, HIGH); 103 | } 104 | interrupts(); 105 | 106 | for (int i=0; i<40; ++i) { 107 | uint32_t lowCycles = cycles[2*i]; 108 | uint32_t highCycles = cycles[2*i+1]; 109 | if ((0 == lowCycles) || (0 == highCycles)) { 110 | addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for pulse")); 111 | dht[sensor].lastresult = false; 112 | return dht[sensor].lastresult; 113 | } 114 | dht_data[i/8] <<= 1; 115 | if (highCycles > lowCycles) { 116 | dht_data[i/8] |= 1; 117 | } 118 | } 119 | 120 | snprintf_P(log, sizeof(log), PSTR("DHT: Received %02X, %02X, %02X, %02X, %02X =? %02X"), 121 | dht_data[0], dht_data[1], dht_data[2], dht_data[3], dht_data[4], (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF); 122 | addLog(LOG_LEVEL_DEBUG, log); 123 | 124 | if (dht_data[4] == ((dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF)) { 125 | dht[sensor].lastresult = true; 126 | } else { 127 | addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Checksum failure")); 128 | dht[sensor].lastresult = false; 129 | } 130 | return dht[sensor].lastresult; 131 | } 132 | 133 | boolean dht_readTempHum(byte sensor, float &t, float &h) 134 | { 135 | if (!dht[sensor].h) { 136 | t = NAN; 137 | h = NAN; 138 | } else { 139 | t = dht[sensor].t; 140 | h = dht[sensor].h; 141 | } 142 | 143 | if (dht_read(sensor)) { 144 | switch (dht[sensor].type) { 145 | case GPIO_DHT11: 146 | h = dht_data[0]; 147 | t = convertTemp(dht_data[2]); 148 | break; 149 | case GPIO_DHT22: 150 | case GPIO_DHT21: 151 | h = dht_data[0]; 152 | h *= 256; 153 | h += dht_data[1]; 154 | h *= 0.1; 155 | t = dht_data[2] & 0x7F; 156 | t *= 256; 157 | t += dht_data[3]; 158 | t *= 0.1; 159 | if (dht_data[2] & 0x80) { 160 | t *= -1; 161 | } 162 | t = convertTemp(t); 163 | break; 164 | } 165 | if (!isnan(t)) { 166 | dht[sensor].t = t; 167 | } 168 | if (!isnan(h)) { 169 | dht[sensor].h = h; 170 | } 171 | } 172 | return (!isnan(t) && !isnan(h)); 173 | } 174 | 175 | boolean dht_setup(byte pin, byte type) 176 | { 177 | boolean success = false; 178 | 179 | if (dht_sensors < DHT_MAX_SENSORS) { 180 | dht[dht_sensors].pin = pin; 181 | dht[dht_sensors].type = type; 182 | dht_sensors++; 183 | success = true; 184 | } 185 | return success; 186 | } 187 | 188 | void dht_init() 189 | { 190 | char log[LOGSZ]; 191 | 192 | dht_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. 193 | 194 | for (byte i = 0; i < dht_sensors; i++) { 195 | pinMode(dht[i].pin, INPUT_PULLUP); 196 | dht[i].lastreadtime = -MIN_INTERVAL; 197 | switch (dht[i].type) { 198 | case GPIO_DHT11: 199 | strcpy_P(dht[i].stype, PSTR("DHT11")); 200 | break; 201 | case GPIO_DHT21: 202 | strcpy_P(dht[i].stype, PSTR("AM2301")); 203 | break; 204 | case GPIO_DHT22: 205 | strcpy_P(dht[i].stype, PSTR("DHT22")); 206 | } 207 | if (dht_sensors > 1) { 208 | snprintf_P(dht[i].stype, sizeof(dht[i].stype), PSTR("%s-%02d"), dht[i].stype, dht[i].pin); 209 | } 210 | } 211 | 212 | snprintf_P(log, sizeof(log), PSTR("DHT: Max clock cycles %d"), dht_maxcycles); 213 | addLog(LOG_LEVEL_DEBUG, log); 214 | } 215 | 216 | /*********************************************************************************************\ 217 | * Presentation 218 | \*********************************************************************************************/ 219 | 220 | void dht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 221 | { 222 | char stemp1[10]; 223 | char stemp2[10]; 224 | float t; 225 | float h; 226 | 227 | byte dsxflg = 0; 228 | for (byte i = 0; i < dht_sensors; i++) { 229 | if (dht_readTempHum(i, t, h)) { // Read temperature 230 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp1); 231 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, stemp2); 232 | // snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":%s, \"Humidity\":%s}"), 233 | // svalue, dhtstype, stemp1, stemp2); 234 | snprintf_P(svalue, ssvalue, JSON_SNS_TEMPHUM, svalue, dht[i].stype, stemp1, stemp2); 235 | *djson = 1; 236 | #ifdef USE_DOMOTICZ 237 | if (!dsxflg) { 238 | domoticz_sensor2(stemp1, stemp2); 239 | dsxflg++; 240 | } 241 | #endif // USE_DOMOTICZ 242 | } 243 | } 244 | } 245 | 246 | #ifdef USE_WEBSERVER 247 | String dht_webPresent() 248 | { 249 | String page = ""; 250 | char stemp[10]; 251 | char sensor[80]; 252 | float t; 253 | float h; 254 | 255 | for (byte i = 0; i < dht_sensors; i++) { 256 | if (dht_readTempHum(i, t, h)) { 257 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp); 258 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, dht[i].stype, stemp, tempUnit()); 259 | page += sensor; 260 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, stemp); 261 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_HUM, dht[i].stype, stemp); 262 | page += sensor; 263 | } 264 | } 265 | return page; 266 | } 267 | #endif // USE_WEBSERVER 268 | #endif // USE_DHT 269 | -------------------------------------------------------------------------------- /sonoff/xsns_ds18b20.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_ds18b20.ino - DS18B20 temperature sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_DS18B20 21 | /*********************************************************************************************\ 22 | * DS18B20 - Temperature 23 | * 24 | * Source: Marinus vd Broek https://github.com/ESP8266nu/ESPEasy and AlexTransit (CRC) 25 | \*********************************************************************************************/ 26 | 27 | float dsb_mt = 0; 28 | 29 | uint8_t dsb_reset() 30 | { 31 | uint8_t r; 32 | uint8_t retries = 125; 33 | 34 | pinMode(pin[GPIO_DSB], INPUT); 35 | do { // wait until the wire is high... just in case 36 | if (--retries == 0) { 37 | return 0; 38 | } 39 | delayMicroseconds(2); 40 | } while (!digitalRead(pin[GPIO_DSB])); 41 | pinMode(pin[GPIO_DSB], OUTPUT); 42 | digitalWrite(pin[GPIO_DSB], LOW); 43 | delayMicroseconds(492); // Dallas spec. = Min. 480uSec. Arduino 500uSec. 44 | pinMode(pin[GPIO_DSB], INPUT); // Float 45 | delayMicroseconds(40); 46 | r = !digitalRead(pin[GPIO_DSB]); 47 | delayMicroseconds(420); 48 | return r; 49 | } 50 | 51 | uint8_t dsb_read_bit(void) 52 | { 53 | uint8_t r; 54 | 55 | pinMode(pin[GPIO_DSB], OUTPUT); 56 | digitalWrite(pin[GPIO_DSB], LOW); 57 | delayMicroseconds(3); 58 | pinMode(pin[GPIO_DSB], INPUT); // let pin float, pull up will raise 59 | delayMicroseconds(10); 60 | r = digitalRead(pin[GPIO_DSB]); 61 | delayMicroseconds(53); 62 | return r; 63 | } 64 | 65 | uint8_t dsb_read(void) 66 | { 67 | uint8_t bitMask; 68 | uint8_t r = 0; 69 | 70 | for (bitMask = 0x01; bitMask; bitMask <<= 1) { 71 | if (dsb_read_bit()) { 72 | r |= bitMask; 73 | } 74 | } 75 | return r; 76 | } 77 | 78 | void dsb_write_bit(uint8_t v) 79 | { 80 | if (v & 1) { 81 | digitalWrite(pin[GPIO_DSB], LOW); 82 | pinMode(pin[GPIO_DSB], OUTPUT); 83 | delayMicroseconds(10); 84 | digitalWrite(pin[GPIO_DSB], HIGH); 85 | delayMicroseconds(55); 86 | } else { 87 | digitalWrite(pin[GPIO_DSB], LOW); 88 | pinMode(pin[GPIO_DSB], OUTPUT); 89 | delayMicroseconds(65); 90 | digitalWrite(pin[GPIO_DSB], HIGH); 91 | delayMicroseconds(5); 92 | } 93 | } 94 | 95 | void dsb_write(uint8_t ByteToWrite) 96 | { 97 | uint8_t bitMask; 98 | 99 | for (bitMask = 0x01; bitMask; bitMask <<= 1) { 100 | dsb_write_bit((bitMask & ByteToWrite) ? 1 : 0); 101 | } 102 | } 103 | 104 | uint8 dsb_crc(uint8 inp, uint8 crc) 105 | { 106 | inp ^= crc; 107 | crc = 0; 108 | if (inp & 0x1) crc ^= 0x5e; 109 | if (inp & 0x2) crc ^= 0xbc; 110 | if (inp & 0x4) crc ^= 0x61; 111 | if (inp & 0x8) crc ^= 0xc2; 112 | if (inp & 0x10) crc ^= 0x9d; 113 | if (inp & 0x20) crc ^= 0x23; 114 | if (inp & 0x40) crc ^= 0x46; 115 | if (inp & 0x80) crc ^= 0x8c; 116 | return crc; 117 | } 118 | 119 | void dsb_readTempPrep() 120 | { 121 | dsb_reset(); 122 | dsb_write(0xCC); // Skip ROM 123 | dsb_write(0x44); // Start conversion 124 | } 125 | 126 | boolean dsb_readTemp(float &t) 127 | { 128 | int16_t DSTemp; 129 | byte msb, lsb, crc, sign = 1; 130 | 131 | if (!dsb_mt) { 132 | t = NAN; 133 | } else { 134 | t = dsb_mt; 135 | } 136 | 137 | if (!dsb_read_bit()) { //check measurement end 138 | addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor busy")); 139 | return !isnan(t); 140 | } 141 | /* 142 | dsb_reset(); 143 | dsb_write(0xCC); // Skip ROM 144 | dsb_write(0x44); // Start conversion 145 | delay(800); 146 | */ 147 | dsb_reset(); 148 | dsb_write(0xCC); // Skip ROM 149 | dsb_write(0xBE); // Read scratchpad 150 | lsb = dsb_read(); 151 | msb = dsb_read(); 152 | crc = dsb_crc(lsb, crc); 153 | crc = dsb_crc(msb, crc); 154 | crc = dsb_crc(dsb_read(), crc); 155 | crc = dsb_crc(dsb_read(), crc); 156 | crc = dsb_crc(dsb_read(), crc); 157 | crc = dsb_crc(dsb_read(), crc); 158 | crc = dsb_crc(dsb_read(), crc); 159 | crc = dsb_crc(dsb_read(), crc); 160 | crc = dsb_crc(dsb_read(), crc); 161 | dsb_reset(); 162 | if (crc) { //check crc 163 | addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor CRC error")); 164 | } else { 165 | DSTemp = (msb << 8) + lsb; 166 | if (DSTemp > 2047) { 167 | DSTemp = (~DSTemp) +1; 168 | sign = -1; 169 | } 170 | t = convertTemp((float)sign * DSTemp * 0.0625); 171 | } 172 | if (!isnan(t)) dsb_mt = t; 173 | return !isnan(t); 174 | } 175 | 176 | /*********************************************************************************************\ 177 | * Presentation 178 | \*********************************************************************************************/ 179 | 180 | void dsb_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 181 | { 182 | char stemp1[10]; 183 | float t; 184 | 185 | if (dsb_readTemp(t)) { // Check if read failed 186 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp1); 187 | snprintf_P(svalue, ssvalue, PSTR("%s, \"DS18B20\":{\"Temperature\":%s}"), svalue, stemp1); 188 | *djson = 1; 189 | #ifdef USE_DOMOTICZ 190 | domoticz_sensor1(stemp1); 191 | #endif // USE_DOMOTICZ 192 | } 193 | } 194 | 195 | #ifdef USE_WEBSERVER 196 | String dsb_webPresent() 197 | { 198 | // Needs TelePeriod to refresh data (Do not do it here as it takes too much time) 199 | String page = ""; 200 | float st; 201 | 202 | if (dsb_readTemp(st)) { // Check if read failed 203 | char stemp[10]; 204 | char sensor[80]; 205 | 206 | dtostrf(st, 1, sysCfg.flag.temperature_resolution, stemp); 207 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, "DS18B20", stemp, tempUnit()); 208 | page += sensor; 209 | } 210 | dsb_readTempPrep(); 211 | return page; 212 | } 213 | #endif // USE_WEBSERVER 214 | #endif // USE_DS18B20 215 | -------------------------------------------------------------------------------- /sonoff/xsns_ds18x20.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_ds18x20.ino - DS18x20 temperature sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Heiko Krupp and Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_DS18x20 21 | /*********************************************************************************************\ 22 | * DS18B20 - Temperature 23 | \*********************************************************************************************/ 24 | 25 | #define DS18S20_CHIPID 0x10 26 | #define DS18B20_CHIPID 0x28 27 | #define MAX31850_CHIPID 0x3B 28 | 29 | #define W1_SKIP_ROM 0xCC 30 | #define W1_CONVERT_TEMP 0x44 31 | #define W1_READ_SCRATCHPAD 0xBE 32 | 33 | #define DS18X20_MAX_SENSORS 8 34 | 35 | #include 36 | 37 | OneWire *ds = NULL; 38 | 39 | uint8_t ds18x20_addr[DS18X20_MAX_SENSORS][8]; 40 | uint8_t ds18x20_idx[DS18X20_MAX_SENSORS]; 41 | uint8_t ds18x20_snsrs = 0; 42 | char dsbstype[9]; 43 | 44 | void ds18x20_init() 45 | { 46 | ds = new OneWire(pin[GPIO_DSB]); 47 | } 48 | 49 | void ds18x20_search() 50 | { 51 | uint8_t num_sensors=0; 52 | uint8_t sensor = 0; 53 | uint8_t i; 54 | 55 | ds->reset_search(); 56 | for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) { 57 | if (!ds->search(ds18x20_addr[num_sensors])) { 58 | ds->reset_search(); 59 | break; 60 | } 61 | // If CRC Ok and Type DS18S20, DS18B20 or MAX31850 62 | if ((OneWire::crc8(ds18x20_addr[num_sensors], 7) == ds18x20_addr[num_sensors][7]) && 63 | ((ds18x20_addr[num_sensors][0]==DS18S20_CHIPID) || (ds18x20_addr[num_sensors][0]==DS18B20_CHIPID) || (ds18x20_addr[num_sensors][0]==MAX31850_CHIPID))) { 64 | num_sensors++; 65 | } 66 | } 67 | for (int i = 0; i < num_sensors; i++) { 68 | ds18x20_idx[i] = i; 69 | } 70 | for (int i = 0; i < num_sensors; i++) { 71 | for (int j = i + 1; j < num_sensors; j++) { 72 | if (uint32_t(ds18x20_addr[ds18x20_idx[i]]) > uint32_t(ds18x20_addr[ds18x20_idx[j]])) { 73 | std::swap(ds18x20_idx[i], ds18x20_idx[j]); 74 | } 75 | } 76 | } 77 | ds18x20_snsrs = num_sensors; 78 | } 79 | 80 | uint8_t ds18x20_sensors() 81 | { 82 | return ds18x20_snsrs; 83 | } 84 | 85 | String ds18x20_address(uint8_t sensor) 86 | { 87 | char addrStr[20]; 88 | uint8_t i; 89 | 90 | for (i = 0; i < 8; i++) { 91 | sprintf(addrStr+2*i, "%02X", ds18x20_addr[ds18x20_idx[sensor]][i]); 92 | } 93 | return String(addrStr); 94 | } 95 | 96 | void ds18x20_convert() 97 | { 98 | ds->reset(); 99 | ds->write(W1_SKIP_ROM); // Address all Sensors on Bus 100 | ds->write(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end 101 | // delay(750); // 750ms should be enough for 12bit conv 102 | } 103 | 104 | boolean ds18x20_read(uint8_t sensor, float &t) 105 | { 106 | byte data[12]; 107 | int8_t sign = 1; 108 | uint8_t i = 0; 109 | float temp9 = 0.0; 110 | uint8_t present = 0; 111 | 112 | t = NAN; 113 | 114 | ds->reset(); 115 | ds->select(ds18x20_addr[ds18x20_idx[sensor]]); 116 | ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad 117 | 118 | for (i = 0; i < 9; i++) { 119 | data[i] = ds->read(); 120 | } 121 | if (OneWire::crc8(data, 8) == data[8]) { 122 | switch(ds18x20_addr[ds18x20_idx[sensor]][0]) { 123 | case DS18S20_CHIPID: // DS18S20 124 | /* 125 | // App_note AN162.pdf page 9 126 | int temp_lsb, temp_msb; 127 | temp_msb = data[1]; // Sign byte + lsbit 128 | temp_lsb = data[0]; // Temp data plus lsb 129 | if (temp_msb <= 0x80) temp_lsb = (temp_lsb/2); // Shift to get whole degree 130 | temp_msb = temp_msb & 0x80; // Mask all but the sign bit 131 | if (temp_msb >= 0x80) { // Negative temperature 132 | temp_lsb = (~temp_lsb)+1; // Twos complement 133 | temp_lsb = (temp_lsb/2); // Shift to get whole degree 134 | temp_lsb = ((-1)*temp_lsb); // Add sign bit 135 | } 136 | t = (int)temp_lsb; // Temperature in whole degree 137 | */ 138 | if (data[1] > 0x80) { 139 | data[0] = (~data[0]) +1; 140 | sign = -1; // App-Note fix possible sign error 141 | } 142 | if (data[0] & 1) { 143 | temp9 = ((data[0] >> 1) + 0.5) * sign; 144 | } else { 145 | temp9 = (data[0] >> 1) * sign; 146 | } 147 | t = convertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); 148 | break; 149 | case DS18B20_CHIPID: // DS18B20 150 | case MAX31850_CHIPID: // MAX31850 151 | uint16_t temp12 = (data[1] << 8) + data[0]; 152 | if (temp12 > 2047) { 153 | temp12 = (~temp12) +1; 154 | sign = -1; 155 | } 156 | t = convertTemp(sign * temp12 * 0.0625); 157 | break; 158 | } 159 | } 160 | return (!isnan(t)); 161 | } 162 | 163 | /*********************************************************************************************\ 164 | * Presentation 165 | \*********************************************************************************************/ 166 | 167 | void ds18x20_type(uint8_t sensor) 168 | { 169 | strcpy_P(dsbstype, PSTR("DS18x20")); 170 | switch(ds18x20_addr[ds18x20_idx[sensor]][0]) { 171 | case DS18S20_CHIPID: 172 | strcpy_P(dsbstype, PSTR("DS18S20")); 173 | break; 174 | case DS18B20_CHIPID: 175 | strcpy_P(dsbstype, PSTR("DS18B20")); 176 | break; 177 | case MAX31850_CHIPID: 178 | strcpy_P(dsbstype, PSTR("MAX31850")); 179 | break; 180 | } 181 | } 182 | 183 | void ds18x20_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 184 | { 185 | char stemp1[10]; 186 | char stemp2[10]; 187 | float t; 188 | 189 | byte dsxflg = 0; 190 | for (byte i = 0; i < ds18x20_sensors(); i++) { 191 | if (ds18x20_read(i, t)) { // Check if read failed 192 | ds18x20_type(i); 193 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp2); 194 | if (!dsxflg) { 195 | snprintf_P(svalue, ssvalue, PSTR("%s, \"DS18x20\":{"), svalue); 196 | *djson = 1; 197 | stemp1[0] = '\0'; 198 | } 199 | dsxflg++; 200 | snprintf_P(svalue, ssvalue, PSTR("%s%s\"DS%d\":{\"Type\":\"%s\", \"Address\":\"%s\", \"Temperature\":%s}"), 201 | svalue, stemp1, i +1, dsbstype, ds18x20_address(i).c_str(), stemp2); 202 | strcpy(stemp1, ", "); 203 | #ifdef USE_DOMOTICZ 204 | if (dsxflg == 1) domoticz_sensor1(stemp2); 205 | #endif // USE_DOMOTICZ 206 | } 207 | } 208 | if (dsxflg) { 209 | snprintf_P(svalue, ssvalue, PSTR("%s}"), svalue); 210 | } 211 | } 212 | 213 | #ifdef USE_WEBSERVER 214 | String ds18x20_webPresent() 215 | { 216 | String page = ""; 217 | char stemp[10]; 218 | char stemp2[16]; 219 | char sensor[80]; 220 | float t; 221 | 222 | for (byte i = 0; i < ds18x20_sensors(); i++) { 223 | if (ds18x20_read(i, t)) { // Check if read failed 224 | ds18x20_type(i); 225 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp); 226 | snprintf_P(stemp2, sizeof(stemp2), PSTR("%s-%d"), dsbstype, i +1); 227 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, stemp2, stemp, tempUnit()); 228 | page += sensor; 229 | } 230 | } 231 | ds18x20_search(); // Check for changes in sensors number 232 | ds18x20_convert(); // Start Conversion, takes up to one second 233 | return page; 234 | } 235 | #endif // USE_WEBSERVER 236 | #endif // USE_DS18x20 237 | -------------------------------------------------------------------------------- /sonoff/xsns_htu21.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_htu21.ino - HTU21 temperature and humidity sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Heiko Krupp and Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_I2C 21 | #ifdef USE_HTU 22 | /*********************************************************************************************\ 23 | * HTU21 - Temperature and Humidy 24 | * 25 | * Source: Heiko Krupp 26 | \*********************************************************************************************/ 27 | 28 | #define HTU21_ADDR 0x40 29 | 30 | #define SI7013_CHIPID 0x0D 31 | #define SI7020_CHIPID 0x14 32 | #define SI7021_CHIPID 0x15 33 | #define HTU21_CHIPID 0x32 34 | 35 | #define HTU21_READTEMP 0xE3 36 | #define HTU21_READHUM 0xE5 37 | #define HTU21_WRITEREG 0xE6 38 | #define HTU21_READREG 0xE7 39 | #define HTU21_RESET 0xFE 40 | #define HTU21_HEATER_WRITE 0x51 41 | #define HTU21_HEATER_READ 0x11 42 | #define HTU21_SERIAL2_READ1 0xFC /* Read 3rd two Serial bytes */ 43 | #define HTU21_SERIAL2_READ2 0xC9 /* Read 4th two Serial bytes */ 44 | 45 | #define HTU21_HEATER_ON 0x04 46 | #define HTU21_HEATER_OFF 0xFB 47 | 48 | #define HTU21_RES_RH12_T14 0x00 // Default 49 | #define HTU21_RES_RH8_T12 0x01 50 | #define HTU21_RES_RH10_T13 0x80 51 | #define HTU21_RES_RH11_T11 0x81 52 | 53 | #define HTU21_CRC8_POLYNOM 0x13100 54 | 55 | uint8_t htuaddr; 56 | uint8_t htutype = 0; 57 | uint8_t delayT; 58 | uint8_t delayH = 50; 59 | char htustype[7]; 60 | 61 | uint8_t check_crc8(uint16_t data) 62 | { 63 | for (uint8_t bit = 0; bit < 16; bit++) { 64 | if (data & 0x8000) { 65 | data = (data << 1) ^ HTU21_CRC8_POLYNOM; 66 | } else { 67 | data <<= 1; 68 | } 69 | } 70 | return data >>= 8; 71 | } 72 | 73 | uint8_t htu21_readDeviceID(void) 74 | { 75 | uint16_t deviceID = 0; 76 | uint8_t checksum = 0; 77 | 78 | Wire.beginTransmission(HTU21_ADDR); 79 | Wire.write(HTU21_SERIAL2_READ1); 80 | Wire.write(HTU21_SERIAL2_READ2); 81 | Wire.endTransmission(); 82 | 83 | Wire.requestFrom(HTU21_ADDR, 3); 84 | deviceID = Wire.read() << 8; 85 | deviceID |= Wire.read(); 86 | checksum = Wire.read(); 87 | if (check_crc8(deviceID) == checksum) { 88 | deviceID = deviceID >> 8; 89 | } else { 90 | deviceID = 0; 91 | } 92 | return (uint8_t)deviceID; 93 | } 94 | 95 | void htu21_setRes(uint8_t resolution) 96 | { 97 | uint8_t current = i2c_read8(HTU21_ADDR, HTU21_READREG); 98 | current &= 0x7E; // Replace current resolution bits with 0 99 | current |= resolution; // Add new resolution bits to register 100 | i2c_write8(HTU21_ADDR, HTU21_WRITEREG, current); 101 | } 102 | 103 | void htu21_reset(void) 104 | { 105 | Wire.beginTransmission(HTU21_ADDR); 106 | Wire.write(HTU21_RESET); 107 | Wire.endTransmission(); 108 | delay(15); // Reset takes 15ms 109 | } 110 | 111 | void htu21_heater(uint8_t heater) 112 | { 113 | uint8_t current = i2c_read8(HTU21_ADDR, HTU21_READREG); 114 | 115 | switch(heater) 116 | { 117 | case HTU21_HEATER_ON : current |= heater; 118 | break; 119 | case HTU21_HEATER_OFF : current &= heater; 120 | break; 121 | default : current &= heater; 122 | break; 123 | } 124 | i2c_write8(HTU21_ADDR, HTU21_WRITEREG, current); 125 | } 126 | 127 | boolean htu21_init() 128 | { 129 | htu21_reset(); 130 | htu21_heater(HTU21_HEATER_OFF); 131 | htu21_setRes(HTU21_RES_RH12_T14); 132 | return true; 133 | } 134 | 135 | float htu21_readHumidity(void) 136 | { 137 | uint8_t checksum = 0; 138 | uint16_t sensorval = 0; 139 | float humidity = 0.0; 140 | 141 | Wire.beginTransmission(HTU21_ADDR); 142 | Wire.write(HTU21_READHUM); 143 | if (Wire.endTransmission() != 0) { 144 | return 0.0; // In case of error 145 | } 146 | delay(delayH); // Sensor time at max resolution 147 | 148 | Wire.requestFrom(HTU21_ADDR, 3); 149 | if (3 <= Wire.available()) { 150 | sensorval = Wire.read() << 8; // MSB 151 | sensorval |= Wire.read(); // LSB 152 | checksum = Wire.read(); 153 | } 154 | if (check_crc8(sensorval) != checksum) { 155 | return 0.0; // Checksum mismatch 156 | } 157 | 158 | sensorval ^= 0x02; // clear status bits 159 | humidity = 0.001907 * (float)sensorval - 6; 160 | 161 | if (humidity > 100) { 162 | return 100.0; 163 | } 164 | if (humidity < 0) { 165 | return 0.01; 166 | } 167 | 168 | return humidity; 169 | } 170 | 171 | float htu21_readTemperature() 172 | { 173 | uint8_t checksum=0; 174 | uint16_t sensorval=0; 175 | float t; 176 | 177 | Wire.beginTransmission(HTU21_ADDR); 178 | Wire.write(HTU21_READTEMP); 179 | if (Wire.endTransmission() != 0) { 180 | return 0.0; // In case of error 181 | } 182 | delay(delayT); // Sensor time at max resolution 183 | 184 | Wire.requestFrom(HTU21_ADDR, 3); 185 | if (3 == Wire.available()) { 186 | sensorval = Wire.read() << 8; // MSB 187 | sensorval |= Wire.read(); // LSB 188 | checksum = Wire.read(); 189 | } 190 | if (check_crc8(sensorval) != checksum) { 191 | return 0.0; // Checksum mismatch 192 | } 193 | 194 | t = convertTemp(0.002681 * (float)sensorval - 46.85); 195 | return t; 196 | } 197 | 198 | float htu21_compensatedHumidity(float humidity, float temperature) 199 | { 200 | if(humidity == 0.00 && temperature == 0.00) { 201 | return 0.0; 202 | } 203 | if(temperature > 0.00 && temperature < 80.00) { 204 | return (-0.15)*(25-temperature)+humidity; 205 | } 206 | } 207 | 208 | uint8_t htu_detect() 209 | { 210 | if (htutype) { 211 | return true; 212 | } 213 | 214 | char log[LOGSZ]; 215 | boolean success = false; 216 | 217 | htuaddr = HTU21_ADDR; 218 | htutype = htu21_readDeviceID(); 219 | success = htu21_init(); 220 | switch (htutype) { 221 | case HTU21_CHIPID: 222 | strcpy_P(htustype, PSTR("HTU21")); 223 | delayT=50; 224 | delayH=16; 225 | break; 226 | case SI7013_CHIPID: 227 | strcpy_P(htustype, PSTR("SI7013")); 228 | delayT=12; 229 | delayH=23; 230 | break; 231 | case SI7020_CHIPID: 232 | strcpy_P(htustype, PSTR("SI7020")); 233 | delayT=12; 234 | delayH=23; 235 | break; 236 | case SI7021_CHIPID: 237 | strcpy_P(htustype, PSTR("SI7021")); 238 | delayT=12; 239 | delayH=23; 240 | break; 241 | default: 242 | strcpy_P(htustype, PSTR("T/RH?")); 243 | delayT=50; 244 | delayH=23; 245 | } 246 | if (success) { 247 | snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), htustype, htuaddr); 248 | addLog(LOG_LEVEL_DEBUG, log); 249 | } else { 250 | htutype = 0; 251 | } 252 | return success; 253 | } 254 | 255 | /*********************************************************************************************\ 256 | * Presentation 257 | \*********************************************************************************************/ 258 | 259 | void htu_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 260 | { 261 | if (!htutype) { 262 | return; 263 | } 264 | 265 | char stemp1[10]; 266 | char stemp2[10]; 267 | 268 | float t = htu21_readTemperature(); 269 | float h = htu21_readHumidity(); 270 | h = htu21_compensatedHumidity(h, t); 271 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, stemp1); 272 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, stemp2); 273 | snprintf_P(svalue, ssvalue, JSON_SNS_TEMPHUM, svalue, htustype, stemp1, stemp2); 274 | *djson = 1; 275 | #ifdef USE_DOMOTICZ 276 | domoticz_sensor2(stemp1, stemp2); 277 | #endif // USE_DOMOTICZ 278 | } 279 | 280 | #ifdef USE_WEBSERVER 281 | String htu_webPresent() 282 | { 283 | String page = ""; 284 | if (htutype) { 285 | char stemp[10]; 286 | char sensor[80]; 287 | 288 | float t_htu21 = htu21_readTemperature(); 289 | float h_htu21 = htu21_readHumidity(); 290 | h_htu21 = htu21_compensatedHumidity(h_htu21, t_htu21); 291 | dtostrf(t_htu21, 1, sysCfg.flag.temperature_resolution, stemp); 292 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, htustype, stemp, tempUnit()); 293 | page += sensor; 294 | dtostrf(h_htu21, 1, sysCfg.flag.humidity_resolution, stemp); 295 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_HUM, htustype, stemp); 296 | page += sensor; 297 | } 298 | return page; 299 | } 300 | #endif // USE_WEBSERVER 301 | #endif // USE_HTU 302 | #endif // USE_I2C 303 | 304 | -------------------------------------------------------------------------------- /sonoff/xsns_sht1x.ino: -------------------------------------------------------------------------------- 1 | /* 2 | xsns_sht1x.ino - SHT1x temperature and sensor support for Sonoff-Tasmota 3 | 4 | Copyright (C) 2017 Theo Arends 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | #ifdef USE_I2C 21 | #ifdef USE_SHT 22 | /*********************************************************************************************\ 23 | * SHT1x - Temperature and Humidy 24 | * 25 | * Reading temperature and humidity takes about 320 milliseconds! 26 | * Source: Marinus vd Broek https://github.com/ESP8266nu/ESPEasy 27 | \*********************************************************************************************/ 28 | 29 | enum { 30 | SHT1X_CMD_MEASURE_TEMP = B00000011, 31 | SHT1X_CMD_MEASURE_RH = B00000101, 32 | SHT1X_CMD_SOFT_RESET = B00011110 33 | }; 34 | 35 | uint8_t sht_sda_pin; 36 | uint8_t sht_scl_pin; 37 | uint8_t shttype = 0; 38 | 39 | boolean sht_reset() 40 | { 41 | pinMode(sht_sda_pin, INPUT_PULLUP); 42 | pinMode(sht_scl_pin, OUTPUT); 43 | delay(11); 44 | for (byte i = 0; i < 9; i++) { 45 | digitalWrite(sht_scl_pin, HIGH); 46 | digitalWrite(sht_scl_pin, LOW); 47 | } 48 | boolean success = sht_sendCommand(SHT1X_CMD_SOFT_RESET); 49 | delay(11); 50 | return success; 51 | } 52 | 53 | boolean sht_sendCommand(const byte cmd) 54 | { 55 | pinMode(sht_sda_pin, OUTPUT); 56 | // Transmission Start sequence 57 | digitalWrite(sht_sda_pin, HIGH); 58 | digitalWrite(sht_scl_pin, HIGH); 59 | digitalWrite(sht_sda_pin, LOW); 60 | digitalWrite(sht_scl_pin, LOW); 61 | digitalWrite(sht_scl_pin, HIGH); 62 | digitalWrite(sht_sda_pin, HIGH); 63 | digitalWrite(sht_scl_pin, LOW); 64 | // Send the command (address must be 000b) 65 | shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); 66 | // Wait for ACK 67 | boolean ackerror = false; 68 | digitalWrite(sht_scl_pin, HIGH); 69 | pinMode(sht_sda_pin, INPUT_PULLUP); 70 | if (digitalRead(sht_sda_pin) != LOW) { 71 | ackerror = true; 72 | } 73 | digitalWrite(sht_scl_pin, LOW); 74 | delayMicroseconds(1); // Give the sensor time to release the data line 75 | if (digitalRead(sht_sda_pin) != HIGH) { 76 | ackerror = true; 77 | } 78 | if (ackerror) { 79 | shttype = 0; 80 | addLog_P(LOG_LEVEL_DEBUG, PSTR("SHT1X: Sensor did not ACK command")); 81 | } 82 | return (!ackerror); 83 | } 84 | 85 | boolean sht_awaitResult() 86 | { 87 | // Maximum 320ms for 14 bit measurement 88 | for (byte i = 0; i < 16; i++) { 89 | if (LOW == digitalRead(sht_sda_pin)) { 90 | return true; 91 | } 92 | delay(20); 93 | } 94 | addLog_P(LOG_LEVEL_DEBUG, PSTR("SHT1X: Data not ready")); 95 | shttype = 0; 96 | return false; 97 | } 98 | 99 | int sht_readData() 100 | { 101 | int val = 0; 102 | 103 | // Read most significant byte 104 | val = shiftIn(sht_sda_pin, sht_scl_pin, 8); 105 | val <<= 8; 106 | // Send ACK 107 | pinMode(sht_sda_pin, OUTPUT); 108 | digitalWrite(sht_sda_pin, LOW); 109 | digitalWrite(sht_scl_pin, HIGH); 110 | digitalWrite(sht_scl_pin, LOW); 111 | pinMode(sht_sda_pin, INPUT_PULLUP); 112 | // Read least significant byte 113 | val |= shiftIn(sht_sda_pin, sht_scl_pin, 8); 114 | // Keep DATA pin high to skip CRC 115 | digitalWrite(sht_scl_pin, HIGH); 116 | digitalWrite(sht_scl_pin, LOW); 117 | return val; 118 | } 119 | 120 | boolean sht_readTempHum(float &t, float &h) 121 | { 122 | float tempRaw; 123 | float humRaw; 124 | float rhLinear; 125 | 126 | t = NAN; 127 | h = NAN; 128 | 129 | if (!sht_reset()) { 130 | return false; 131 | } 132 | if (!sht_sendCommand(SHT1X_CMD_MEASURE_TEMP)) { 133 | return false; 134 | } 135 | if (!sht_awaitResult()) { 136 | return false; 137 | } 138 | tempRaw = sht_readData(); 139 | // Temperature conversion coefficients from SHT1X datasheet for version 4 140 | const float d1 = -39.7; // 3.5V 141 | const float d2 = 0.01; // 14-bit 142 | t = d1 + (tempRaw * d2); 143 | if (!sht_sendCommand(SHT1X_CMD_MEASURE_RH)) { 144 | return false; 145 | } 146 | if (!sht_awaitResult()) { 147 | return false; 148 | } 149 | humRaw = sht_readData(); 150 | // Temperature conversion coefficients from SHT1X datasheet for version 4 151 | const float c1 = -2.0468; 152 | const float c2 = 0.0367; 153 | const float c3 = -1.5955E-6; 154 | const float t1 = 0.01; 155 | const float t2 = 0.00008; 156 | rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; 157 | h = (t - 25) * (t1 + t2 * humRaw) + rhLinear; 158 | t = convertTemp(t); 159 | return (!isnan(t) && !isnan(h)); 160 | } 161 | 162 | boolean sht_readCharTempHum(char* temp, char* hum) 163 | { 164 | float t; 165 | float h; 166 | 167 | boolean success = sht_readTempHum(t, h); 168 | dtostrf(t, 1, sysCfg.flag.temperature_resolution, temp); 169 | dtostrf(h, 1, sysCfg.flag.humidity_resolution, hum); 170 | return success; 171 | } 172 | 173 | boolean sht_detect() 174 | { 175 | if (shttype) { 176 | return true; 177 | } 178 | 179 | float t; 180 | float h; 181 | 182 | sht_sda_pin = pin[GPIO_I2C_SDA]; 183 | sht_scl_pin = pin[GPIO_I2C_SCL]; 184 | if (sht_readTempHum(t, h)) { 185 | shttype = 1; 186 | addLog_P(LOG_LEVEL_DEBUG, PSTR("I2C: SHT1X found")); 187 | } else { 188 | Wire.begin(sht_sda_pin, sht_scl_pin); 189 | shttype = 0; 190 | } 191 | return shttype; 192 | } 193 | 194 | /*********************************************************************************************\ 195 | * Presentation 196 | \*********************************************************************************************/ 197 | 198 | void sht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) 199 | { 200 | if (!shttype) { 201 | return; 202 | } 203 | 204 | char stemp[10]; 205 | char shum[10]; 206 | 207 | if (sht_readCharTempHum(stemp, shum)) { 208 | snprintf_P(svalue, ssvalue, JSON_SNS_TEMPHUM, svalue, "SHT1X", stemp, shum); 209 | *djson = 1; 210 | #ifdef USE_DOMOTICZ 211 | domoticz_sensor2(stemp, shum); 212 | #endif // USE_DOMOTICZ 213 | } 214 | } 215 | 216 | #ifdef USE_WEBSERVER 217 | String sht_webPresent() 218 | { 219 | String page = ""; 220 | if (shttype) { 221 | char stemp[10]; 222 | char shum[10]; 223 | 224 | if (sht_readCharTempHum(stemp, shum)) { 225 | char sensor[80]; 226 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, "SHT1X", stemp, tempUnit()); 227 | page += sensor; 228 | snprintf_P(sensor, sizeof(sensor), HTTP_SNS_HUM, "SHT1X", shum); 229 | page += sensor; 230 | } 231 | } 232 | return page; 233 | } 234 | #endif // USE_WEBSERVER 235 | #endif // USE_SHT 236 | #endif // USE_I2C 237 | 238 | --------------------------------------------------------------------------------