This commit is contained in:
arendst 2017-01-28 14:41:01 +01:00
parent cde3982940
commit d392cf2f77
25 changed files with 11483 additions and 0 deletions

BIN
api/arduino/sonoff.ino.bin Normal file

Binary file not shown.

15
api/upload-arduino.php Normal file
View File

@ -0,0 +1,15 @@
<?php
// mkdir and chmod arduino folder to 777
//
//var_dump($_FILES);
$image = basename($_FILES["file"]["name"]);
$target_file = "arduino/".$image;
$hostname = $_SERVER['SERVER_NAME'];
if (move_uploaded_file($_FILES["file"]["tmp_name"], $target_file)) {
echo "The file $image has been uploaded to OTA server $hostname. \n";
} else {
echo "Sorry, there was an error uploading your file $image to OTA server $hostname. \n";
}
?>

112
arduino/espupload.py Normal file
View File

@ -0,0 +1,112 @@
#!/usr/bin/python
#
# espupload by Theo Arends - 20170103
#
# Uploads binary file to OTA server
#
# Execute: espupload -i <Host_IP_address> -p <Host_port> -f <sketch.bin>
#
# Needs pycurl
# - pip install pycurl
import sys
import os
import optparse
import logging
import pycurl
HOST_ADDR = "domus1"
HOST_PORT = 80
HOST_URL = "/api/upload-arduino.php"
def upload(hostAddr, hostPort, filename):
url = 'http://%s:%d%s' % (hostAddr, hostPort, HOST_URL)
c = pycurl.Curl()
c.setopt(c.URL, url)
# The "Expect:" is there to suppress "Expect: 100-continue" behaviour that is
# the default in libcurl when posting large bodies (and fails on lighttpd).
c.setopt(c.HTTPHEADER, ["Expect:"])
c.setopt(c.HTTPPOST, [('file', (c.FORM_FILE, filename, )), ])
c.perform()
c.close()
def parser():
parser = optparse.OptionParser(
usage = "%prog [options]",
description = "Upload image to over the air Host server for the esp8266 module with OTA support."
)
# destination ip and port
group = optparse.OptionGroup(parser, "Destination")
group.add_option("-i", "--host_ip",
dest = "host_ip",
action = "store",
help = "Host IP Address.",
default = HOST_ADDR
)
group.add_option("-p", "--host_port",
dest = "host_port",
type = "int",
help = "Host server ota Port. Default 80",
default = HOST_PORT
)
parser.add_option_group(group)
# image
group = optparse.OptionGroup(parser, "Image")
group.add_option("-f", "--file",
dest = "image",
help = "Image file.",
metavar="FILE",
default = None
)
parser.add_option_group(group)
# output group
group = optparse.OptionGroup(parser, "Output")
group.add_option("-d", "--debug",
dest = "debug",
help = "Show debug output. And override loglevel with debug.",
action = "store_true",
default = False
)
parser.add_option_group(group)
(options, args) = parser.parse_args()
return options
# end parser
def main(args):
# get options
options = parser()
# adapt log level
loglevel = logging.WARNING
if (options.debug):
loglevel = logging.DEBUG
# end if
# logging
logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S')
logging.debug("Options: %s", str(options))
if (not options.host_ip or not options.image):
logging.critical("Not enough arguments.")
return 1
# end if
if not os.path.exists(options.image):
logging.critical('Sorry: the file %s does not exist', options.image)
return 1
# end if
upload(options.host_ip, options.host_port, options.image)
# end main
if __name__ == '__main__':
sys.exit(main(sys.argv))
# end if

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
# ESP8266 platform
# ------------------------------
# For more info:
# https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification
name=ESP8266 Modules
version=2.2.0
compiler.warning_flags=-w
compiler.warning_flags.none=-w
compiler.warning_flags.default=
compiler.warning_flags.more=-Wall
compiler.warning_flags.all=-Wall -Wextra
build.lwip_lib=-llwip_gcc
build.lwip_flags=-DLWIP_OPEN_SRC
compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/
compiler.sdk.path={runtime.platform.path}/tools/sdk
compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/lwip/include" "-I{build.path}/core"
compiler.c.cmd=xtensa-lx106-elf-gcc
compiler.c.flags=-c {compiler.warning_flags} -Os -g -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=gnu99 -ffunction-sections -fdata-sections
compiler.S.cmd=xtensa-lx106-elf-gcc
compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls
compiler.c.elf.flags=-g {compiler.warning_flags} -Os -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/ld" "-T{build.flash_ld}" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,register_chipv6_phy
compiler.c.elf.cmd=xtensa-lx106-elf-gcc
compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lpp -lnet80211 -lwpa -lcrypto -lmain -lwps -laxtls -lsmartconfig -lmesh -lwpa2 {build.lwip_lib} -lstdc++
compiler.cpp.cmd=xtensa-lx106-elf-g++
compiler.cpp.flags=-c {compiler.warning_flags} -Os -g -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD -ffunction-sections -fdata-sections
compiler.as.cmd=xtensa-lx106-elf-as
compiler.ar.cmd=xtensa-lx106-elf-ar
compiler.ar.flags=cru
compiler.elf2hex.cmd=esptool
compiler.elf2hex.flags=
compiler.size.cmd=xtensa-lx106-elf-size
compiler.esptool.cmd=esptool
compiler.esptool.cmd.windows=esptool.exe
# This can be overriden in boards.txt
build.extra_flags=-DESP8266
# These can be overridden in platform.local.txt
compiler.c.extra_flags=
compiler.c.elf.extra_flags=
compiler.S.extra_flags=
compiler.cpp.extra_flags=
compiler.ar.extra_flags=
compiler.objcopy.eep.extra_flags=
compiler.elf2hex.extra_flags=
## generate file with git version number
## needs bash, git, and echo
## windows-compatible version may be added later
## Compile c files
recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
## Compile c++ files
recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
## Compile S files
recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
## Create archives
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/arduino.ar" "{object_file}"
## Combine gc-sections, archives, and objects
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{build.path}/arduino.ar" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}"
## Create eeprom
recipe.objcopy.eep.pattern=
## Create hex
#recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
recipe.objcopy.hex.pattern="{runtime.tools.esptool.path}/{compiler.esptool.cmd}" -eo "{runtime.platform.path}/bootloaders/eboot/eboot.elf" -bo "{build.path}/{build.project_name}.bin" -bm {build.flash_mode} -bf {build.flash_freq} -bz {build.flash_size} -bs .text -bp 4096 -ec -eo "{build.path}/{build.project_name}.elf" -bs .irom0.text -bs .text -bs .data -bs .rodata -bc -ec
## Save hex
recipe.output.tmp_file={build.project_name}.bin
recipe.output.save_file={build.project_name}.{build.variant}.bin
## Compute size
recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf"
recipe.size.regex=^(?:\.irom0\.text|\.text|\.data|\.rodata|)\s+([0-9]+).*
recipe.size.regex.data=^(?:\.data|\.rodata|\.bss)\s+([0-9]+).*
#recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).*
# ------------------------------
tools.esptool.cmd=esptool
tools.esptool.cmd.windows=esptool.exe
tools.esptool.path={runtime.tools.esptool.path}
tools.esptool.network_cmd=python
tools.esptool.network_cmd.windows=python.exe
tools.esptool.upload.protocol=esp
tools.esptool.upload.params.verbose=-vv
tools.esptool.upload.params.quiet=
tools.esptool.upload.pattern="{path}/{cmd}" {upload.verbose} -cd {upload.resetmethod} -cb {upload.speed} -cp "{serial.port}" -ca 0x00000 -cf "{build.path}/{build.project_name}.bin"
tools.esptool.upload.network_pattern="{network_cmd}" "{runtime.platform.path}/tools/espota.py" -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin"
tools.mkspiffs.cmd=mkspiffs
tools.mkspiffs.cmd.windows=mkspiffs.exe
tools.mkspiffs.path={runtime.tools.mkspiffs.path}
tools.espupload.cmd=python
tools.espupload.cmd.windows=python.exe
tools.espupload.path={runtime.platform.path}/tools
tools.espupload.upload.protocol=espupload
tools.espupload.upload.params.verbose=
tools.espupload.upload.params.quiet=
tools.espupload.upload.pattern="{cmd}" "{path}/espupload.py" -f "{build.path}/{build.project_name}.bin"

33
sonoff/README.md Normal file
View File

@ -0,0 +1,33 @@
## Sonoff-MQTT-OTA-Arduino - TASMOTA NextGen
Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE.
Current version is **3.9.4** - See ```_releasenotes.ino``` for change information.
- This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic.
- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```.
- After reboot select config menu again or use commands ```GPIOs``` and ```GPIO``` to change GPIO with desired sensor.
- Some features still need to be ironed out.
<img alt="Sonoff" src="https://github.com/arendst/arendst.github.io/blob/master/media/sonoffbasic.jpg" width="250" align="right" />
See [Wiki](https://github.com/arendst/Sonoff-MQTT-OTA-Arduino/wiki) for more information.<br />
See [Community](https://groups.google.com/d/forum/sonoffusers) for forum and more user experience.
Starting with version 2.0.0 the following devices are supported:
- [iTead Sonoff Basic](http://sonoff.itead.cc/en/products/sonoff/sonoff-basic)
- [iTead Sonoff RF](http://sonoff.itead.cc/en/products/sonoff/sonoff-rf)
- [iTead Sonoff SV](https://www.itead.cc/sonoff-sv.html)
<img alt="Sonoff" src="https://github.com/arendst/arendst.github.io/blob/master/media/sonoff_th.jpg" width="250" align="right" />
- [iTead Sonoff TH10/TH16 with temperature sensor](http://sonoff.itead.cc/en/products/sonoff/sonoff-th)
- [iTead Sonoff Dual](http://sonoff.itead.cc/en/products/sonoff/sonoff-dual)
- [iTead Sonoff Pow](http://sonoff.itead.cc/en/products/sonoff/sonoff-pow)
- [iTead Sonoff 4CH](http://sonoff.itead.cc/en/products/sonoff/sonoff-4ch)
- [iTead S20 Smart Socket](http://sonoff.itead.cc/en/products/residential/s20-socket)
- [iTead Slampher](http://sonoff.itead.cc/en/products/residential/slampher-rf)
- [iTead Sonoff Touch](http://sonoff.itead.cc/en/products/residential/sonoff-touch)
- [iTead Sonoff Led](http://sonoff.itead.cc/en/products/appliances/sonoff-led)
- [iTead 1 Channel Switch 5V / 12V](https://www.itead.cc/smart-home/inching-self-locking-wifi-wireless-switch.html)
- [iTead Motor Clockwise/Anticlockwise](https://www.itead.cc/smart-home/motor-reversing-wifi-wireless-switch.html)
- [Electrodragon IoT Relay Board](http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/)
<img alt="Sonoff" src="https://github.com/arendst/arendst.github.io/blob/master/media/sonofftoucheu.jpg" height="280" align="left" />
<img alt="Sonoff" src="https://github.com/arendst/arendst.github.io/blob/master/media/sonoff4ch.jpg" height="250" align="right" />

514
sonoff/_releasenotes.ino Normal file
View File

@ -0,0 +1,514 @@
/* 3.9.4 20170127
* Fix Sonoff Dual Relay switching (#287)
*
* 3.9.3 20170127
* Add confirmation before Restart via webpage
* Expand Domoticz Configuration webpage with Key, Switch and Sensor Index and
* add commands DomoticzSwitchIdx and DomoticzSensorIdx (#86) (#174) (#219)
* Fix default DHT11 sensor driver selection
* Fix LedPower status after button press (#279)
* Add command Sleep 0 - 250 mSec for optional light sleep mode to lower energy consumption (#272)
* (Expect overall button/key/switch misses and wrong values on Sonoff Pow)
* Add Hue brightness extension (#281)
* Fix Hue brightness and change to call by reference (#283)
*
* 3.9.2 20170124
* Add confirmation before Reset Configuration via webpage (#244)
* Add WS2812 features (see Wiki commands)
*
* 3.9.1 20170124
* Change PowerOnState function to only trigger when Power On (and not just restart) (#238)
* Move HLW interrupts back to RAM and make WS2812_DMA optional as it generates Exception on Pow (#264)
* Add charset=utf-8 to webpages (#266)
* Update Hue emulation (#268)
* Fix status module number
* Add support for domoticz Dimmer on Sonoff_Led and WS2812
* Fix possible ESP8285 flash problem by updating Flash Chip Mode to DOUT during web upload
*
* 3.2.6a 20170120
* Fix Sonoff Pow compile error (#255)
* Move HLW interrupts back to ROM (Needed for WS2812 DMA interrupts)
* Removed all IO config from user_config.h as this will be done by commands or webpage
* Removed MessageFormat and supports JSON only except POWER/LIGHT status
* Add command LedPower to control main led (#247)
* Add more FriendlyNames for Hue (#254)
* Add DMA support for WS2812 when using pin 3 while other pins work just as well in my case...
* Add HUE emulation for Alexa (#229)
* Add basic WS2812 support (#229)
* Fix Wemo when MQTT is disabled (#245)
* Revert ButtonTopic and change SwitchTopic1 - 4 to one SwitchTopic
* Rename MqttUnits to Units
* Add Mqtt command to enable/disable MQTT
*
* 3.2.2a 20170115
* Add dynamic (Sonoff) Module, user GPIO and sensor selection (one size fits (almost) all)
* Add support for Sonoff LED
* Add Seriallog disable after 600 seconds for Sonoff Dual and 4 Channel
* Add ButtonTopic2 - 4, SwitchTopic1 - 4 and SwitchRetain
*
* 3.2.2 20170113
* Fix PowerOnState 2 functionality after re-applying power (#230)
*
* 3.2.1 20170113
* Fix some failed command decoding (#228)
* Removed passwords from status messages (#216)
*
* 3.2.0 20170111
* Add I2C BH1750 sensor (#222)
* Sensor rewrite preparing for online selection
*
* 3.1.16 20170109
* Fix Domoticz possible error condition
* Remove Wifi password from connection message (#216)
* Add Configure Other menu item to web page (#209)
* Add command FriendlyName, field Friendly Name and define FRIENDLY_NAME to be used by Alexa
* eliminating current use of MQTT_CLIENT_ID (#209)
* Add friendlyname to webpage replacing former hostname
*
* 3.1.15 20170108
* Fix Domoticz send key regression with Toggle command
*
* 3.1.14 20170107
* Add support for command TOGGLE (define MQTT_CMND_TOGGLE) when ButtonTopic is in use and not equal to Topic (#207)
*
* 3.1.13 20170107
* Fix web console command input when SUB_PREFIX contains '/' (#152)
* Add command response to web command (#200)
* Add option to disable MQTT as define USE_MQTT in user_config.h (#200)
*
* 3.1.12 20170106
* Add OTA retry to solve possible HTTP transient errors (#204)
* Fix MQTT host discovery
*
* 3.1.11 20170105
* Add mDNS to advertise webserver as <hostname>.local/
*
* 3.1.10 20170105
* Fix ButtonTopic when SUB_PREFIX = PUB_PREFIX
* Add workaround for possible MQTT queueing when SUB_PREFIX = PUB_PREFIX
* Add optional MQTT host discovery using define USE_DISCOVERY in user_config.h (#115)
*
* 3.1.9 20170104
* Fix Power Blink start position (toggled)
* Change PulseTime increments: 1 .. 111 in 0.1 sec (max 11 seconds) and 112 .. 64900 in seconds (= 12 seconds until 18 hours) (#188)
* Add support for SUB_PREFIX = PUB_PREFIX (#190)
*
* 3.1.8 20170103
* Add retain flag to LWT offline and only send "tele/sonoff/LWT Offline" (#179)
* Change retained LWT Online message to only send "tele/sonoff/LWT Online"
*
* 3.1.7 20161231
* Add retained message LWT Online when sonoff makes MQTT connection (#179)
*
* 3.1.6 20161230
* Add blinking using commands BlinkTime, BlinkCount and Power Blink|3|BlinkOff|4 (#165)
*
* 3.1.5 20161228
* Fix serial space command exception (28)
*
* 3.1.4 20161227
* Fix MQTT subscribe regression exception (3) (#162)
* Fix serial empty command exception (28)
*
* 3.1.3 20161225
* Extent Domoticz configuration webpage with optional indices (#153)
* Fix multi relay legacy tele message from tele/sonoff/2/POWER to tele/sonoff/POWER2
* Add support for iTead Motor Clockwise/Anticlockwise
*
* 3.1.2 20161224
* Extent command PowerOnState with toggle at power on (option 2 is now option 3!) (#156)
*
* 3.1.1 20161223
* Add support for Sonoff Touch and Sonoff 4CH (#40)
* Update DomoticzIdx and DomoticzKeyIdx with relay/key index (DomoticzIdx1/DomoticzKeyIdx1)
* Add command PowerOnState to control relay(s) at power on (#154)
*
* 3.1.0 20161221
* Add Sonoff Pow measurement smoothing
* Fix serial command topic preamble error (#151)
* Fix 2.x to 3.x migration inconsistencies (#146)
*
* 3.0.9 20161218
* Add Sonoff Pow voltage reading when relay is on but no load present (#123)
*
* 3.0.8 20161218
* Add temperature conversion to Fahrenheit as option in user_config.h (TEMP_CONVERSION) (#145)
*
* 3.0.7 20161217
* Add user_config_override.h to be used by user to override some defaults in user_config.h (#58)
* Fix Sonoff Pow low power (down to 4W) intermittent measurements (#123)
*
* 3.0.6 20161217
* Fix MQTT_CLIENT_ID starting with % sign as in "%06X" (#142)
* Add auto power off after PulseTime * 0.1 Sec to relay 1 (#134)
*
* 3.0.5 20161215
* Add more control over LED with command LedState options (#136, #143)
* LED_OFF (0), LED_POWER (1), LED_MQTTSUB (2), LED_POWER_MQTTSUB (3), LED_MQTTPUB (4), LED_POWER_MQTTPUB (5), LED_MQTT (6), LED_POWER_MQTT (7)
* Add option WIFI_RETRY (4) to command WifiConfig to allow connection retry to other AP without restart (#73)
*
* 3.0.4 20161211
* Fix intermittent Domoticz update misses (#133)
*
* 3.0.3 20161210
* Fix compiler warnings (#132)
* Remove redundant code
* Fix Domoticz pushbutton support
*
* 3.0.2 20161209
* Add pushbutton to SwitchMode (#130)
*
* 3.0.1 20161209
* Fix initial config
*
* 3.0.0 20161208
* Migrate and clean-up flash layout
* Settings from version 2.x are saved but settings from version 3.x can not be used with version 2.x
* Change SEND_TELEMETRY_RSSI to SEND_TELEMETRY_WIFI and add AP and SSID to telemetry
* Split long JSON messages
* Fix inconsistent status messages
* Fix all status messages to return JSON if enabled
* Remove relay index in cmnd/sonoff/<relay>/POWER now changed
* to cmnd/sonoff/POWER for single relay units
* and cmnd/sonoff/POWER<relay> for multi relay units like Sonoff dual
* Add retain option to Power/Light status controlled by command PowerRetain On|Off (#126)
*
* 2.1.2 20161204
* Add support for second wifi AP (#73)
* Update command WifiConfig
* Fix possible WifiManager hang
*
* 2.1.1a 20161203
* Fix scan for wifi networks if WeMo is enabled
* Fix syslog setting using web page
*
* 2.1.1 20161202
* Add support for ElectroDragon second relay and button (only toggle with optional ButtonTopic) (#110)
*
* 2.1.0 20161202
* Add optional EXPERIMENTAL TLS to MQTT (#49)
* Fix MQTT payload handling (#111)
* Optimzed WeMo code
*
* 2.0.21a 20161201
* Fix WeMo PowerPlug emulation
*
* 2.0.21 20161130
* Add Belkin WeMo PowerPlug emulation enabled with USE_WEMO_EMULATION in user_config.h (Heiko Krupp) (#105, #109)
*
* 2.0.20 20161130
* Relax MQTTClient naming but only allows hexadecimal uppercase numbers (#107)
* Add I2C support with command I2CScan
* Add I2C sensor driver for HTU21 as alternate sensor using TH10/16 connectors (Heiko Krupp) (#105)
* Add I2C sensor driver for BMP085/BMP180/BMP280/BME280 as alternate sensor using TH10/16 connectors
*
* 2.0.19a 20161127
* Add support for ButtonTopic and ButtonRetain to wall switch function
* Add pullup to SWITCH_PIN and command SwitchMode to syntax
*
* 2.0.18 20161126
* Add SUB_PREFIX multi level support allowing 'cmnd' or 'cmnd/level2/level3'
* Add wall switch function to GPIO14 and command SwitchMode (Alex Scott) (#103)
*
* 2.0.17 20161123
* Calibrate HLWPCAL from 12345 to 12530
* Add alternative sensor driver DHT2 using Adafruit DHT library
* Add define MESSAGE_FORMAT to user_config.h
* Throttle console messages
* Shorten JSON messages
* Fix possible Panic
* Fix User mode webserver security
*
* 2.0.16 20161118
* Add alternative sensor driver DS18x20 using OneWire library (#95)
* Change sensor MQTT message from tele/sonoff/TEMPERATURE to tele/sonoff/DHT/TEMPERATURE or
* tele/sonoff/DS18B20/TEMPERATURE or tele/sonoff/DS18x20/1/TEMPERATURE
* Add sensors to root webpage and auto refresh every 4 seconds (#92)
* Add optional JSON messageformat to all telemetry data
* Enforce minimum TelePeriod to be 10 seconds
* Fix Energy Yesterday reset after restart
* Add Energy Today restore after controlled restart
*
* 2.0.15 20161116
* Change TODAY_POWER and PERIOD_POWER to TODAY_ENERGY and PERIOD_ENERGY
* Fix serial regression
* Fix syslog hangs when loghost is unavailable
*
* 2.0.14 20161115
* Add HLW threshold delay
* Fix HLW intermittent current deviation
* Fix button functionality during wificonfig
* Add CRC check to DS18B20 sensor (#88)
*
* 2.0.13 20161113
* Add additional upload error code descriptions
* Add PlatformIO support (#80)
*
* 2.0.12 20161113
* Fix Serial and Web response regression when no MQTT connection available
* Fix Sonoff Dual power telemetric data for second relay
* Removed MQTT password from Information web page
* Hide MQTT password from Configure MQTT web page
*
* 2.0.11 20161111
* Rewrite button and web toggle code
* Fix NTP sync
* Add HLW calibration commands HLWPCAL, HLWUCAL and HLWICAL (need define USE_POWERCALIBRATION)
* Fix power threshold tests
*
* 2.0.10 20161109
* Add additional Domoticz define (#63)
* Add defines MQTT_STATUS_ON and MQTT_STATUS_OFF in user_config.h to select status On/Off string
* Fix status response differences (#65)
* Fix divide by zero exception (#70)
* Fix syslog loop exception
*
* 2.0.9 20161108
* clarify MODULE in user_config.h
* Fix hlw false values
*
* 2.0.8 20161108
* Add initial status after power on
* Seperate driver files
* Fix hlw code and calibrate Pow
* Move user config defines to user_config.h (#61)
*
* 2.0.7 20161030
* Make Ticker mandatory
* Add Domoticz support (Increase MQTT_MAX_PACKET_SIZE to 400) (#54)
* Add command MessageFormat 0|1 to select either legacy or JSON output
*
* 2.0.6 20161024
* Add Sonoff Pow power factor
* Initial support for up to four relays using iTEAD PSB (4Channel)
* - Currently only supports one button (All buttons behave the same)
* - Use command MODEL 4 to select four relay option
* (After first power on it will support 2 relays like Sonoff Dual)
* Fix ledstate
* Add command Status 9 to display Sonoff Pow thresholds
* Add commands PowerLow, PowerHigh, VoltageLow, VoltageHigh, CurrentLow and CurrentHigh for use
* with Sonoff Pow thresholds
*
* 2.0.5 20161018
* Add updates to user_config.h - moved SEND_TELEMETRY_DS18B20 and SEND_TELEMETRY_DHT to module area.
* As Sonoff TH10/16 does not have the logic installed for GPIO04 You'll have to select ONE of both
* Add Sonoff Pow support (experimental until Pow tested)
* Add command Status 8 to display Sonoff Pow energy values
* Add command MqttUnits On|Off to add units to values
* Change web main page header character size
* Change On/Off to ON/OFF status messages to satisfy openHAB
* Change TEMP to TEMPERATURE and HUM to HUMIDITY
*
* 2.0.4 20161009
* Add MQTT_BUTTON_RETAIN, SAVE_DATA and SAVE_STATE defines to user_config.h (#35)
* Update ButtonRetain to remove retained message(s) from broker when turned off
* Add Retain for second relay on Sonoff Dual
* Provide power status messages with device topic index if requested
*
* 2.0.3 20161008
* Update wifi initialization
* Add command BUTTONRETAIN for optional MQTT retain on button press (#35)
* Add command SAVESTATE to disable power state save. May be used with MQTT retain
*
* 2.0.2 20161006
* Fix wifi issue 2186
*
* 2.0.1 20161002
* Fix button press
*
* 2.0.0 20161002
* Update Sonoff TH10/16 sensor pins (My TH10 only has GPIO14 connected)
* Add full support for Sonoff dual
*
* 1.0.35 20160929
* Add more lines to console
* Add timeout and disable MQTT on web upload
* Add command SAVEDATA to control parameter save (for flash wear afficionados) (#30)
*
* 1.0.34 20160926
* Fix button press six and seven
* Add more information to webserver
*
* 1.0.33 20160915
* Better WPS error message
* Separate webserver code from support.ino into webserver.ino
* Fix webserver User by removing unwanted restart option
*
* 1.0.32 20160913
* Add Wifi Protected Setup (WPS) as third option for initial config
* Add command WIFICONFIG replacing deprecated command SMARTCONFIG
* Add option WIFICONFIG 3 to start WPSconfig
* Add option WIFICONFIG 0 to start saved Wifi config tool (WPSconfig, Smartconfig or Wifimanager)
* Change button behaviour - See Wiki
*
* 1.0.31 20160907
* Fix DS18B20 misread if teleperiod = 2
* Tuned sensor code
* Updated prefered ElectroDragon connection to Relay 1 and Button 1
* Moved SONOFF and ELECTRO_DRAGON port config to user_config.h
*
* 1.0.30 20160902
* Fix command TELEPERIOD 0
* Add ESP- tag to UDP log message for easy rsyslogd filtering
* Add ElectroDragon (Relay 2 only) functionality. Select with #define MODULE ELECTRO_DRAGON
* Add ? as null message alternative
* Add DHT temperature and humidity telemetry support. Enable with #define SEND_TELEMETRY_DHT
* Add DS18B20 temperature telemetry support. Enable with #define SEND_TELEMETRY_DS18B20
* Restrict HOSTNAME, MQTTCLIENT, TOPIC and BUTTONTOPIC in topic mode only
*
* 1.0.29 20160831
* Allow UPGRADE, OTAURL, RESTART, RESET, MQTTHOST, MQTTPORT, MQTTUSER, MQTTPASSWORD and WEBSERVER also in group mode
*
* 1.0.28 20160831
* Add webserver state to status 5
* Add optional PUB_PREFIX2 (tele) for telemetry usage
* Add command TELEPERIOD
* Fix syntax message
* Change memory status display
*
* 1.0.27 20160831
* Add sketch flash size
* Add console to webserver
* Add command weblog
* Change WifiManager web pages to minimal
* Change display default hostname and MQTT client id in webserver
* Change HTTP command interface to http://sonoff-1234/cm?cmnd=light 2
* Change HEARTBEAT to UPTIME
*
* 1.0.26 20160829
* Add define USE_WEBSERVER to disable web server code in source
* Add file upload as alternative for ota upload to webserver
* Add information to webserver
* Add command hostname
* Add command logport
* Change HTTP command interface to http://sonoff-1234/cmd?cmnd=light 2
* Change button behaviour with regards to Smartconfig and OTA upload. See README.md
* Enforce default hostname to either "%s-%04d" or user defined without any %
* Enforce default mqtt client id to either "DVES_%06X" or user defined without any %
*
* 1.0.25 20160822
* Remove config system halts to keep ota available
*
* 1.0.24 20160821
* Add test for MQTT_SUBTOPIC
* Change log range to LOG_LEVEL_ALL
* Change MQTT introduction messages
* Moved MQTT_MAX_PACKET_SIZE warning message to introduction messages
*
* 1.0.23 20160821
* Add option USE_SPIFFS to move config from flash to spiffs
* Add webserver with options 0 (off), 1 (user) and 2 (admin)
* Add HTTP command interface (http://sonoff-1234/c?cmnd=light 2)
* Add wifimanager countdown counter
* Add command line webpage
* Add relay control to wifimanager
* Add restart option 99 to force restart
* Fix wifi hostname
* Fix NETBIOS hostname problem by reducing default hostname length
* Fix possible exception if WIFI_HOSTNAME is changed
* Fix upgrade messages
* Reduce memory use by redesigning config routines
* Split syntax message
* Rename define SERIAL_IO to USE_SERIAL
*
* 1.0.22 20160814
* Add all MQTT parameters for configuration
* Add wifimanager to configure Wifi and MQTT via web server
* Change NTP time handling
* Fix Smartconfig parameter buffer overflow
* Fix PlatformIO warnings
*
* 1.0.21 20160808
* Remove semaphore as subscription flooding (more than 15 subscriptions per second) is managed by SDK (LmacRxBlk:1)
* Add optional RTC interrupt (define USE_TICKER) to keep RTC synced during subscription flooding
* Remove heartbeatflag
*
* 1.0.20 20160805
* Add semaphore to handle out of memory when too many subscriptions requested
* Use Daylight Saving (DST) parameters from user_config.h when timezone = 99
* Add status 7 option displaying RTC information
* Add ledstate to status 0
*
* 1.0.19 20160803
* Fix possible MQTT_CLIENT_ID induced Exception(28)
*
* 1.0.18 20160803
* Moved Cfg_Default
* Fix negative data handling
* Remove MQTT information from status 1 and add labels to status 1
* Add mac address to status 5
* Add MQTT ClientId, UserId and Password to status 6
*
* 1.0.17 20160731
* Better variable range checking
* Change ambiguous connection messages
* Add timestamp to serial message
*
* 1.0.16 20160729
* Moved wifi, rtc, syslog and config to support.ino
* Fixed button action when buttontopic is used. Introduced with 1.0.15
* Better buffer overflow checks (strlcpy)
*
* 1.0.15 20160728
* Removed pubsubclient config changes from sonoff.ino as it doesn't work
* reapply MQTT_MAX_PACKET_SIZE 256 and MQTT_KEEPALIVE 120 to PubSubClient.h
* Add status 0 option displaying all status messages
* Change MQTT_MAX_PACKET_SIZE from 1024 to 256
* Add buffer overflow checks (snprintf and strncpy)
* Implemented common string sizes
*
* 1.0.14 20160722
* Seperate user config from sonoff.ino to user_config.h (pucebaboon)
* Change defaults from sidnas2 to domus1
* Add MQTT status message as status 6 (pucebaboon)
* Add status type to message (pucebaboon)
* Add pubsubclient config changes to sonoff.ino (pucebaboon)
*
* 1.0.13 20160702
* Add Ledstate 1 option to show power state on led
*
* 1.0.12 20160529
* Allow disable of button topic using "0"
*
* 1.0.11 20160524
* Provide button response if MQTT connection lost
*
* 1.0.10 20160520
* Add optional button topic to assist external MQTT clients
* Change version notation
* Reset default values
*
* 1.0.9 20160503
* Add more blinks
* Add reset 2 option erasing flash
* Add status 5 option displaying network info
* Add syslog check for Wifi connection
* Resize mqtt_publish log array
* Change Wifi smartconfig active from 100 to 60 seconds
* Update Wifi initialization
*
* 1.0.8 20160430
* Remove use of Wifi config data from SDK
* Add status 3 (syslog info) and status 4 (flash info)
* Add restart option to button (5 quick presses)
*
* 1.0.7 20160420
* Add UDP syslog support
* Change HOST command to MQTTHOST command
* Add commands SYSLOG, SERIALLOG and LOGHOST
* Change hostname to lower case to distinguise between open-sdk version
* Add support for ESP-12F used in my modified wkaku power socket switch
* Fix timezone command
* Add RTC month names for future use
* Modify button code
* Remove initialization errors by better use of MQTT loop
*
* 1.0.6 20160406
* Removed Wifi AP mode (#1)
* Add test for Arduino IDE version >= 1.6.8
* Fix RTC time sync code
*
* 1.0.5 20160310
* Initial public release
* Show debug info by selecting option from IDE Tools Debug port: Serial
*/

2760
sonoff/sonoff.ino Normal file

File diff suppressed because it is too large Load Diff

248
sonoff/sonoff_template.h Normal file
View File

@ -0,0 +1,248 @@
/*********************************************************************************************\
* Template parameters
\*********************************************************************************************/
#define GPIO_SENSOR_START 0
#define GPIO_NONE 0 // Not used
#define GPIO_DHT11 1 // DHT11
#define GPIO_DHT21 2 // DHT21
#define GPIO_AM2301 2 // AM2301
#define GPIO_DHT22 3 // DHT22
#define GPIO_AM2302 3 // AM2302
#define GPIO_AM2321 3 // AM2321
#define GPIO_DSB 4 // Single wire DS18B20 or DS18S20
#define GPIO_I2C_SCL 5 // I2C SCL
#define GPIO_I2C_SDA 6 // I2C SDA
#define GPIO_WS2812 7 // WS2812 Led string
#define GPIO_SWT1 8 // User connected external switches
#define GPIO_SENSOR_END 9
#define GPIO_SWT2 9
#define GPIO_SWT3 10
#define GPIO_SWT4 11
#define GPIO_KEY1 12 // Button usually connected to GPIO0
#define GPIO_KEY2 13
#define GPIO_KEY3 14
#define GPIO_KEY4 15
#define GPIO_REL1 16 // Relays
#define GPIO_REL2 17
#define GPIO_REL3 18
#define GPIO_REL4 19
#define GPIO_REL1_INV 20
#define GPIO_REL2_INV 21
#define GPIO_REL3_INV 22
#define GPIO_REL4_INV 23
#define GPIO_LED1 24 // Leds
#define GPIO_LED2 25
#define GPIO_LED3 26
#define GPIO_LED4 27
#define GPIO_LED1_INV 28
#define GPIO_LED2_INV 29
#define GPIO_LED3_INV 30
#define GPIO_LED4_INV 31
#define GPIO_PWM0 32 // Cold
#define GPIO_PWM1 33 // Warm
#define GPIO_PWM2 34 // Red (swapped with Blue from original)
#define GPIO_PWM3 35 // Green
#define GPIO_PWM4 36 // Blue (swapped with Red from original)
#define GPIO_RXD 37 // Serial interface
#define GPIO_TXD 38 // Serial interface
#define GPIO_HLW_SEL 39 // HLW8012 Sel output (Sonoff Pow)
#define GPIO_HLW_CF1 40 // HLW8012 CF1 voltage / current (Sonoff Pow)
#define GPIO_HLW_CF 41 // HLW8012 CF power (Sonoff Pow)
#define GPIO_USER 42 // User configurable
#define GPIO_MAX 43
/********************************************************************************************/
enum module_t {SONOFF_BASIC, SONOFF_RF, SONOFF_SV, SONOFF_TH, SONOFF_DUAL, SONOFF_POW, SONOFF_4CH, S20, SLAMPHER, SONOFF_TOUCH, SONOFF_LED, CH1, CH4, MOTOR, ELECTRODRAGON, USER_TEST, MAXMODULE};
/********************************************************************************************/
#define MAX_GPIO_PIN 17 // Number of supported GPIO
#define DHT11 11
#define DHT21 21
#define AM2301 21
#define DHT22 22
#define AM2302 22
#define AM2321 22
typedef struct MYIO {
uint8_t io[MAX_GPIO_PIN];
} myio;
typedef struct MYTMPLT {
char name[16];
myio gp;
} mytmplt;
const char sensors[GPIO_SENSOR_END][8] PROGMEM =
{ "None", "DHT11", "AM2301", "DHT22", "DS18x20", "I2C SCL", "I2C SDA", "WS2812", "Switch" };
const mytmplt modules[MAXMODULE] PROGMEM = {
{ "Sonoff Basic", // Sonoff Basic
GPIO_KEY1, // GPIO00 Button
0, 0,
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor
0, 0
},
{ "Sonoff RF", // Sonoff RF
GPIO_KEY1, // GPIO00 Button
0, 0,
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor
0, 0
},
{ "Sonoff SV", // Sonoff SV
GPIO_KEY1, // GPIO00 Button
0, 0,
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor
0, 0
},
{ "Sonoff TH", // Sonoff TH10/16
GPIO_KEY1, // GPIO00 Button
0, 0,
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor
0, 0
},
{ "Sonoff Dual", // Sonoff Dual
0,
GPIO_TXD, // GPIO01 Relay control
0,
GPIO_RXD, // GPIO03 Relay control
0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
0, 0, 0
},
{ "Sonoff Pow", // Sonoff Pow
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0,
GPIO_HLW_SEL, // GPIO05 HLW8012 Sel output
0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_HLW_CF1, // GPIO13 HLW8012 CF1 voltage / current
GPIO_HLW_CF, // GPIO14 HLW8012 CF power
GPIO_LED1, // GPIO15 Green Led (0 = On, 1 = Off)
0
},
{ "Sonoff 4CH", // Sonoff 4CH
GPIO_KEY1, // GPIO00 Button 1
0,
GPIO_USER, // GPIO02 Optional sensor
0,
GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On)
GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On)
0,
GPIO_USER, // GPIO07 Optional sensor
GPIO_USER, // GPIO08 Optional sensor
GPIO_KEY2, // GPIO09 Button 2
GPIO_KEY3, // GPIO10 Button 3
0,
GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_KEY4, // GPIO14 Button 4
GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On)
0
},
{ "S20 Socket", // S20 Smart Socket
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
0, 0, 0
},
{ "Slampher", // Slampher
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
0, 0, 0
},
{ "Sonoff Touch", // Sonoff Touch
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
0, 0, 0
},
{ "Sonoff LED", // Sonoff LED
GPIO_KEY1, // GPIO00 Button
0, 0, 0,
GPIO_PWM3, // GPIO04 Green light
GPIO_PWM2, // GPIO05 Red light
0, 0, 0, 0, 0, 0,
GPIO_PWM0, // GPIO12 Cold light
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
GPIO_PWM1, // GPIO14 Warm light
GPIO_PWM4, // GPIO15 Blue light
0
},
{ "1 Channel", // 1 Channel Inching/Latching Relay
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
0, 0, 0
},
{ "4 Channel", // 4 Channel Inching/Latching Relays
0,
GPIO_TXD, // GPIO01 Relay control
0,
GPIO_RXD, // GPIO03 Relay control
0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
0, 0, 0
},
{ "Motor C/AC", // Motor Clockwise / Anti clockwise
GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
0, 0, 0
},
{ "ElectroDragon", // ElectroDragon IoT Relay Board
GPIO_KEY2, // GPIO00 Button 2
0,
GPIO_KEY1, // GPIO02 Button 1
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On)
GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On)
GPIO_USER, // GPIO14 Optional sensor
0,
GPIO_LED1 // GPIO16 Green/Blue Led (1 = On, 0 = Off)
},
{ "User Test", // Sonoff Basic User Test
GPIO_KEY1, // GPIO00 Button
0, 0,
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor
0, 0
}
};

17
sonoff/support.h Normal file
View File

@ -0,0 +1,17 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __SUPPORT_H__
#define __SUPPORT_H__
#include "user_interface.h"
/* Function prototypes. */
void WIFI_wps_status_cb(wps_cb_status status);
#endif // ifndef __SUPPORT_H__
#ifdef __cplusplus
}
#endif

1005
sonoff/support.ino Normal file

File diff suppressed because it is too large Load Diff

147
sonoff/user_config.h Normal file
View File

@ -0,0 +1,147 @@
/*********************************************************************************************\
* User specific configuration parameters
*
* ATTENTION: Changes to most PARAMETER defines will only override flash settings if you change
* define CFG_HOLDER.
* Most parameters can be changed online using commands via MQTT, WebConsole or serial
*
* Corresponding MQTT/Serial/Console commands in [brackets]
\*********************************************************************************************/
// -- Project -------------------------------------
#define PROJECT "sonoff" // PROJECT is used as the default topic delimiter and OTA file name
// As an IDE restriction it needs to be the same as the main .ino file
#define CFG_HOLDER 0x20161209 // [Reset 1] Change this value to load following default configuration parameters
#define SAVE_DATA 1 // [SaveData] Save changed parameters to Flash (0 = disable, 1 - 3600 seconds)
#define SAVE_STATE 1 // [SaveState] Save changed power state to Flash (0 = disable, 1 = enable)
// -- Wifi ----------------------------------------
#define STA_SSID1 "indebuurt1" // [Ssid1] Wifi SSID
#define STA_PASS1 "VnsqrtnrsddbrN" // [Password1] Wifi password
#define STA_SSID2 "indebuurt2" // [Ssid2] Optional alternate AP Wifi SSID
#define STA_PASS2 "VnsqrtnrsddbrN" // [Password2] Optional alternate AP Wifi password
#define WIFI_HOSTNAME "%s-%04d" // [Hostname] Expands to <MQTT_TOPIC>-<last 4 decimal chars of MAC address>
#define WIFI_CONFIG_TOOL WIFI_WPSCONFIG // [WifiConfig] Default tool if wifi fails to connect
// (WIFI_RESTART, WIFI_SMARTCONFIG, WIFI_MANAGER, WIFI_WPSCONFIG, WIFI_RETRY)
// -- Syslog --------------------------------------
#define SYS_LOG_HOST "domus1" // [LogHost] (Linux) syslog host
#define SYS_LOG_PORT 514 // [LogPort] default syslog UDP port
#define SYS_LOG_LEVEL LOG_LEVEL_NONE // [SysLog]
#define SERIAL_LOG_LEVEL LOG_LEVEL_INFO // [SerialLog]
#define WEB_LOG_LEVEL LOG_LEVEL_INFO // [WebLog]
// -- Ota -----------------------------------------
#define OTA_URL "http://domus1:80/api/arduino/" PROJECT ".ino.bin" // [OtaUrl]
// -- MQTT ----------------------------------------
#define MQTT_USE 1 // [Mqtt] Select default MQTT use (0 = Off, 1 = On)
// !!! TLS uses a LOT OF MEMORY (20k) so be careful to enable other options at the same time !!!
//#define USE_MQTT_TLS // EXPERIMENTAL Use TLS for MQTT connection (+53k code, +20k mem)
// Needs Fingerprint, TLS Port, UserId and Password
#ifdef USE_MQTT_TLS
#define MQTT_HOST "m20.cloudmqtt.com" // [MqttHost]
#define MQTT_FINGERPRINT "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint]
#define MQTT_PORT 20123 // [MqttPort] MQTT TLS port
#define MQTT_USER "cloudmqttuser" // [MqttUser] Mandatory user
#define MQTT_PASS "cloudmqttpassword" // [MqttPassword] Mandatory password
#else
#define MQTT_HOST "domus1" // [MqttHost]
#define MQTT_PORT 1883 // [MqttPort] MQTT port (10123 on CloudMQTT)
#define MQTT_USER "DVES_USER" // [MqttUser] Optional user
#define MQTT_PASS "DVES_PASS" // [MqttPassword] Optional password
#endif
#define MQTT_CLIENT_ID "DVES_%06X" // [MqttClient] Also fall back topic using Chip Id = last 6 characters of MAC address
#define SUB_PREFIX "cmnd" // Sonoff devices subscribe to:- SUB_PREFIX/MQTT_TOPIC and SUB_PREFIX/MQTT_GRPTOPIC
#define PUB_PREFIX "stat" // Sonoff devices publish to:- PUB_PREFIX/MQTT_TOPIC
#define PUB_PREFIX2 "tele" // Sonoff devices publish telemetry data to:- PUB_PREFIX2/MQTT_TOPIC/UPTIME, POWER/LIGHT and TIME
// May be named the same as PUB_PREFIX
#define MQTT_TOPIC PROJECT // [Topic] (unique) MQTT device topic
#define MQTT_GRPTOPIC "sonoffs" // [GroupTopic] MQTT Group topic
#define MQTT_BUTTON_RETAIN 0 // [ButtonRetain] Button may send retain flag (0 = off, 1 = on)
#define MQTT_POWER_RETAIN 0 // [PowerRetain] Power status message may send retain flag (0 = off, 1 = on)
#define MQTT_SWITCH_RETAIN 0 // [SwitchRetain] Switch may send retain flag (0 = off, 1 = on)
#define MQTT_STATUS_OFF "OFF" // Command or Status result when turned off (needs to be a string like "0" or "Off")
#define MQTT_STATUS_ON "ON" // Command or Status result when turned on (needs to be a string like "1" or "On")
#define MQTT_CMND_TOGGLE "TOGGLE" // Command to send when toggling (needs to be a string like "2" or "Toggle")
// -- MQTT - Telemetry ----------------------------
#define TELE_PERIOD 300 // [TelePeriod] Telemetry (0 = disable, 10 - 3600 seconds)
// -- MQTT - Domoticz -----------------------------
#define USE_DOMOTICZ // Enable Domoticz (+5k code, +0.3k mem) - Disable by //
#define DOMOTICZ_IN_TOPIC "domoticz/in" // [DomoticzInTopic]
#define DOMOTICZ_OUT_TOPIC "domoticz/out" // [DomoticzOutTopic]
#define DOMOTICZ_UPDATE_TIMER 0 // [DomoticzUpdateTimer] Send relay status (0 = disable, 1 - 3600 seconds) (Optional)
// -- HTTP ----------------------------------------
#define USE_WEBSERVER // Enable web server and wifi manager (+43k code, +2k mem) - Disable by //
#define FRIENDLY_NAME1 "Sonoff" // [FriendlyName1] Friendlyname up to 32 characters used by webpages and Alexa
#define FRIENDLY_NAME2 "Sonoff2" // [FriendlyName2] Friendlyname up to 32 characters used by Alexa
#define FRIENDLY_NAME3 "Sonoff3" // [FriendlyName3] Friendlyname up to 32 characters used by Alexa
#define FRIENDLY_NAME4 "Sonoff4" // [FriendlyName4] Friendlyname up to 32 characters used by Alexa
#define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin)
// #define USE_WEMO_EMULATION // Enable Belkin WeMo PowerSwitch emulation for Alexa (+4k code, +2k mem)
// #define USE_HUE_EMULATION // Enable Hue Bridge emulation for Alexa (+5k code, +2k mem)
// -- mDNS ----------------------------------------
#define USE_DISCOVERY // Enable mDNS for the following services (+8k code, +0.3k mem)
#define WEBSERVER_ADVERTISE // Provide access to webserver by name <Hostname>.local/
#define MQTT_HOST_DISCOVERY // Find MQTT host server (overrides MQTT_HOST if found)
// -- Time - Up to three NTP servers in your region
#define NTP_SERVER1 "pool.ntp.org"
#define NTP_SERVER2 "nl.pool.ntp.org"
#define NTP_SERVER3 "0.nl.pool.ntp.org"
// -- Time - Start Daylight Saving Time and timezone offset from UTC in minutes
#define TIME_DST Last, Sun, Mar, 2, +120 // Last sunday in march at 02:00 +120 minutes
// -- Time - Start Standard Time and timezone offset from UTC in minutes
#define TIME_STD Last, Sun, Oct, 3, +60 // Last sunday in october 02:00 +60 minutes
// -- Application ---------------------------------
#define APP_TIMEZONE 1 // [Timezone] +1 hour (Amsterdam) (-12 .. 12 = hours from UTC, 99 = use TIME_DST/TIME_STD)
#define APP_LEDSTATE LED_POWER // [LedState] Function of led (LED_OFF, LED_POWER, LED_MQTTSUB, LED_POWER_MQTTSUB, LED_MQTTPUB, LED_POWER_MQTTPUB, LED_MQTT, LED_POWER_MQTT)
#define APP_PULSETIME 0 // [PulseTime] Time in 0.1 Sec to turn off power for relay 1 (0 = disabled)
#define APP_POWERON_STATE 3 // [PowerOnState] Power On Relay state (0 = Off, 1 = On, 2 = Toggle Saved state, 3 = Saved state)
#define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1
#define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000)
#define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec)
#define SWITCH_MODE TOGGLE // [SwitchMode] TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON or PUSHBUTTON_INV (the wall switch state)
#define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with
#define TEMP_CONVERSION 0 // Convert temperature to (0 = Celsius or 1 = Fahrenheit)
#define TEMP_RESOLUTION 1 // Maximum number of decimals (0 - 3) showing sensor Temperature
#define HUMIDITY_RESOLUTION 1 // Maximum number of decimals (0 - 3) showing sensor Humidity
#define PRESSURE_RESOLUTION 1 // Maximum number of decimals (0 - 3) showing sensor Pressure
// -- Sensor code selection -----------------------
//#define USE_DHT2 // Optional using Adafruit DHT library
//#define USE_DS18x20 // Optional using OneWire library for multiple DS18B20 and/or DS18S20
#define USE_I2C // I2C Support (+10k code, 0.2k mem)
#define USE_BH1750 // Add I2C code for BH1750 sensor
#define USE_BMP // Add I2C code for BMP/BME280 sensor
#define USE_HTU // Add I2C code for HTU21 sensor
#define USE_WS2812 // WS2812 Led string support (+8k code, +1k mem)
// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial TXD) (+1k mem)
// When USE_WS2812_DMA is enabled expect Exceptions on Pow
/*********************************************************************************************\
* No user configurable items below
\*********************************************************************************************/
#if defined(USE_WEMO_EMULATION) && defined(USE_HUE_EMULATION)
#error "Select either USE_WEMO_EMULATION or USE_HUE_EMULATION"
#endif
#if (ARDUINO < 10610)
#error "This software is supported with Arduino IDE starting from 1.6.10 and ESP8266 Release 2.3.0"
#endif

View File

@ -0,0 +1,23 @@
/*****************************************************************************************************\
* User specific configuration parameters to override user_config.h
*
* ATTENTION: - Changes to most PARAMETER defines will only override flash settings if you change
* define CFG_HOLDER.
* - Expect compiler warnings when no ifdef/undef/endif sequence is used.
* - You still need to update user_config.h for major defines MODULE and USE_MQTT_TLS.
* - Changing MODULE defines are not being tested for validity as they are in user_config.h.
* - Most parameters can be changed online using commands via MQTT, WebConsole or serial.
* - So I see no use in this but anyway, your on your own.
\*****************************************************************************************************/
// Examples
//#ifdef CFG_HOLDER
//#undef CFG_HOLDER
//#endif
//#define CFG_HOLDER 0x20161210
//#ifdef STA_SSID1
//#undef STA_SSID1
//#endif
//#define STA_SSID1 "yourssid1"

1521
sonoff/webserver.ino Normal file

File diff suppressed because it is too large Load Diff

376
sonoff/xdrv_domoticz.ino Normal file
View File

@ -0,0 +1,376 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_DOMOTICZ
#define DOMOTICZ_MAX_SENSORS 5
const char HTTP_FORM_DOMOTICZ[] PROGMEM =
"<fieldset><legend><b>&nbsp;Domoticz parameters&nbsp;</b></legend><form method='post' action='sv'>"
"<input id='w' name='w' value='4' hidden><input id='r' name='r' value='1' hidden>"
"<br/><table style='width:97%'>"
"<tr><td><b>In topic</b> (" DOMOTICZ_IN_TOPIC ")</td><td style='width:30%'><input id='it' name='it' length=32 placeholder='" DOMOTICZ_IN_TOPIC "' value='{d1}'></td></tr>"
"<tr><td><b>Out topic</b> (" DOMOTICZ_OUT_TOPIC ")</td><td><input id='ot' name='ot' length=32 placeholder='" DOMOTICZ_OUT_TOPIC "' value='{d2}'></td></tr>";
const char domoticz_sensors[DOMOTICZ_MAX_SENSORS][14] PROGMEM =
{ "Temp", "Temp,Hum", "Temp,Hum,Baro", "Power,Energy", "Illuminance" };
int domoticz_update_timer = 0;
byte domoticz_update_flag = 1;
unsigned long getKeyIntValue(const char *json, const char *key)
{
char *p, *b, log[LOGSZ];
int i;
// search key
p = strstr(json, key);
if (!p) return 0;
// search following separator :
b = strchr(p + strlen(key), ':');
if (!b) return 0;
// Only the following chars are allowed between key and separator :
for(i = b - json + strlen(key); i < p-json; i++) {
switch (json[i]) {
case ' ':
case '\n':
case '\t':
case '\r':
continue;
default:
return 0;
}
}
b++;
// Allow integers as string too (used in "svalue" : "9")
while ((b[0] == ' ') || (b[0] == '"')) b++;
// Convert to integer
return atoi(b);
}
void mqtt_publishDomoticzPowerState(byte device)
{
char svalue[MESSZ];
if (sysCfg.domoticz_relay_idx[device -1] && (strlen(sysCfg.domoticz_in_topic) != 0)) {
if ((device < 1) || (device > Maxdevice)) device = 1;
if (sysCfg.module == SONOFF_LED) {
snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"),
sysCfg.domoticz_relay_idx[device -1], sysCfg.led_dimmer[device -1]);
mqtt_publish(sysCfg.domoticz_in_topic, svalue);
}
else if ((device == 1) && (pin[GPIO_WS2812] < 99)) {
snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":2,\"svalue\":\"%d\"}"),
sysCfg.domoticz_relay_idx[device -1], sysCfg.ws_dimmer);
mqtt_publish(sysCfg.domoticz_in_topic, svalue);
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"\"}"),
sysCfg.domoticz_relay_idx[device -1], (power & (0x01 << (device -1))) ? 1 : 0);
mqtt_publish(sysCfg.domoticz_in_topic, svalue);
}
}
void domoticz_updatePowerState(byte device)
{
if (domoticz_update_flag) mqtt_publishDomoticzPowerState(device);
domoticz_update_flag = 1;
}
void domoticz_mqttUpdate()
{
if ((sysCfg.domoticz_update_timer || domoticz_update_timer) && sysCfg.domoticz_relay_idx[0]) {
domoticz_update_timer--;
if (domoticz_update_timer <= 0) {
domoticz_update_timer = sysCfg.domoticz_update_timer;
for (byte i = 1; i <= Maxdevice; i++) mqtt_publishDomoticzPowerState(i);
}
}
}
void domoticz_setUpdateTimer(uint16_t value)
{
domoticz_update_timer = value;
}
void domoticz_mqttSubscribe()
{
if (sysCfg.domoticz_relay_idx[0] && (strlen(sysCfg.domoticz_out_topic) != 0)) {
char stopic[TOPSZ];
snprintf_P(stopic, sizeof(stopic), PSTR("%s/#"), sysCfg.domoticz_out_topic); // domoticz topic
mqttClient.subscribe(stopic);
mqttClient.loop(); // Solve LmacRxBlk:1 messages
}
}
boolean domoticz_update()
{
return domoticz_update_flag;
}
boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf)
{
char log[LOGSZ], stemp1[10];
unsigned long idx = 0;
int16_t nvalue, found = 0;
domoticz_update_flag = 1;
if (!strncmp(topicBuf, sysCfg.domoticz_out_topic, strlen(sysCfg.domoticz_out_topic)) != 0) {
if (sdataBuf < 20) return 1;
idx = getKeyIntValue(dataBuf,"\"idx\"");
nvalue = getKeyIntValue(dataBuf,"\"nvalue\"");
snprintf_P(log, sizeof(log), PSTR("DMTZ: idx %d, nvalue %d"), idx, nvalue);
addLog(LOG_LEVEL_DEBUG_MORE, log);
if (nvalue >= 0 && nvalue <= 2) {
for (byte i = 0; i < Maxdevice; i++) {
if ((idx > 0) && (idx == sysCfg.domoticz_relay_idx[i])) {
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1);
if (nvalue == 2) {
nvalue = getKeyIntValue(dataBuf,"\"svalue1\"");
if ((pin[GPIO_WS2812] < 99) && (sysCfg.ws_dimmer == nvalue)) return 1;
if ((sysCfg.module == SONOFF_LED) && (sysCfg.led_dimmer[i] == nvalue)) return 1;
snprintf_P(topicBuf, stopicBuf, PSTR("%s/%s/DIMMER%s"),
SUB_PREFIX, sysCfg.mqtt_topic, (Maxdevice > 1) ? stemp1 : "");
snprintf_P(dataBuf, sdataBuf, PSTR("%d"), nvalue);
found = 1;
} else {
if (((power >> i) &1) == nvalue) return 1;
snprintf_P(topicBuf, stopicBuf, PSTR("%s/%s/%s%s"),
SUB_PREFIX, sysCfg.mqtt_topic, sysCfg.mqtt_subtopic, (Maxdevice > 1) ? stemp1 : "");
snprintf_P(dataBuf, sdataBuf, PSTR("%d"), nvalue);
found = 1;
}
break;
}
}
}
if (!found) return 1;
snprintf_P(log, sizeof(log), PSTR("DMTZ: Receive topic %s, data %s"), topicBuf, dataBuf);
addLog(LOG_LEVEL_DEBUG_MORE, log);
domoticz_update_flag = 0;
}
return 0;
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
void domoticz_commands(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue)
{
if (!strcmp(type,"DOMOTICZINTOPIC")) {
if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_in_topic))) {
strlcpy(sysCfg.domoticz_in_topic, (payload == 1) ? DOMOTICZ_IN_TOPIC : dataBuf, sizeof(sysCfg.domoticz_in_topic));
restartflag = 2;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzInTopic\":\"%s\"}"), sysCfg.domoticz_in_topic);
}
else if (!strcmp(type,"DOMOTICZOUTTOPIC")) {
if ((data_len > 0) && (data_len < sizeof(sysCfg.domoticz_out_topic))) {
strlcpy(sysCfg.domoticz_out_topic, (payload == 1) ? DOMOTICZ_OUT_TOPIC : dataBuf, sizeof(sysCfg.domoticz_out_topic));
restartflag = 2;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzOutTopic\":\"%s\"}"), sysCfg.domoticz_out_topic);
}
else if (!strcmp(type,"DOMOTICZIDX") && (index > 0) && (index <= Maxdevice)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_relay_idx[index -1] = payload;
restartflag = 2;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzIdx%d\":%d}"), index, sysCfg.domoticz_relay_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZKEYIDX") && (index > 0) && (index <= Maxdevice)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_key_idx[index -1] = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzKeyIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZSWITCHIDX") && (index > 0) && (index <= Maxdevice)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_switch_idx[index -1] = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSwitchIdx%d\":%d}"), index, sysCfg.domoticz_key_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZSENSORIDX") && (index > 0) && (index <= DOMOTICZ_MAX_SENSORS)) {
if ((data_len > 0) && (payload >= 0)) {
sysCfg.domoticz_sensor_idx[index -1] = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzSensorIdx%d\":%d}"), index, sysCfg.domoticz_sensor_idx[index -1]);
}
else if (!strcmp(type,"DOMOTICZUPDATETIMER")) {
if ((data_len > 0) && (payload >= 0) && (payload < 3601)) {
sysCfg.domoticz_update_timer = payload;
}
snprintf_P(svalue, ssvalue, PSTR("{\"DomoticzUpdateTimer\":%d}"), sysCfg.domoticz_update_timer);
}
}
boolean domoticz_button(byte key, byte device, byte state, byte svalflg)
{
if ((sysCfg.domoticz_key_idx[device -1] || sysCfg.domoticz_switch_idx[device -1]) && (svalflg)) {
char svalue[MESSZ];
snprintf_P(svalue, sizeof(svalue), PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"),
(key) ? sysCfg.domoticz_switch_idx[device -1] : sysCfg.domoticz_key_idx[device -1], (state) ? (state == 2) ? "Toggle" : "On" : "Off");
mqtt_publish(sysCfg.domoticz_in_topic, svalue);
return 1;
} else {
return 0;
}
}
/*********************************************************************************************\
* Sensors
\*********************************************************************************************/
uint8_t dom_hum_stat(char *hum)
{
uint8_t h = atoi(hum);
return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1;
}
void dom_sensor(byte idx, char *data)
{
char dmess[64];
if (sysCfg.domoticz_sensor_idx[idx] && (strlen(sysCfg.domoticz_in_topic) != 0)) {
snprintf_P(dmess, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"),
sysCfg.domoticz_sensor_idx[idx], data);
mqtt_publish(sysCfg.domoticz_in_topic, dmess);
}
}
void domoticz_sensor1(char *temp)
{
dom_sensor(0, temp);
}
void domoticz_sensor2(char *temp, char *hum)
{
char data[16];
snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temp, hum, dom_hum_stat(hum));
dom_sensor(1, data);
}
void domoticz_sensor3(char *temp, char *hum, char *baro)
{
char data[32];
snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temp, hum, dom_hum_stat(hum), baro);
dom_sensor(2, data);
}
void domoticz_sensor4(uint16_t power, char *energy)
{
char data[16];
snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy);
dom_sensor(3, data);
}
void domoticz_sensor5(uint16_t lux)
{
char data[8];
snprintf_P(data, sizeof(data), PSTR("%d"), lux);
dom_sensor(4, data);
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void handleDomoticz()
{
if (_httpflag == HTTP_USER) {
handleRoot();
return;
}
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle Domoticz config"));
char stemp[20];
String page = FPSTR(HTTP_HEAD);
page.replace("{v}", "Configure Domoticz");
page += FPSTR(HTTP_FORM_DOMOTICZ);
page.replace("{d1}", String(sysCfg.domoticz_in_topic));
page.replace("{d2}", String(sysCfg.domoticz_out_topic));
for (int i = 0; i < Maxdevice; i++) {
page += F("<tr><td><b>Idx {1</b></td></td><td><input id='r{1' name='r{1' length=8 placeholder='0' value='{2'></td></tr>");
page += F("<tr><td><b>Key idx {1</b></td><td><input id='k{1' name='k{1' length=8 placeholder='0' value='{3'></td></tr>");
page += F("<tr><td><b>Switch idx {1</b></td><td><input id='s{1' name='s{1' length=8 placeholder='0' value='{4'></td></tr>");
page.replace("{1", String(i +1));
page.replace("{2", String((int)sysCfg.domoticz_relay_idx[i]));
page.replace("{3", String((int)sysCfg.domoticz_key_idx[i]));
page.replace("{4", String((int)sysCfg.domoticz_switch_idx[i]));
}
for (int i = 0; i < DOMOTICZ_MAX_SENSORS; i++) {
page += F("<tr><td><b>Sensor idx {1</b> - {2</td><td><input id='l{1' name='l{1' length=8 placeholder='0' value='{4'></td></tr>");
page.replace("{1", String(i +1));
snprintf_P(stemp, sizeof(stemp), domoticz_sensors[i]);
page.replace("{2", stemp);
page.replace("{4", String((int)sysCfg.domoticz_sensor_idx[i]));
}
page += F("<tr><td><b>Update timer</b> (" STR(DOMOTICZ_UPDATE_TIMER) ")</td><td><input id='ut' name='ut' length=32 placeholder='" STR(DOMOTICZ_UPDATE_TIMER) "' value='{d7}'</td></tr>");
page.replace("{d7}", String((int)sysCfg.domoticz_update_timer));
page += F("</table>");
page += FPSTR(HTTP_FORM_END);
page += FPSTR(HTTP_BTN_CONF);
showPage(page);
}
void domoticz_saveSettings()
{
char log[LOGSZ], stemp[20];
strlcpy(sysCfg.domoticz_in_topic, (!strlen(webServer->arg("it").c_str())) ? DOMOTICZ_IN_TOPIC : webServer->arg("it").c_str(), sizeof(sysCfg.domoticz_in_topic));
strlcpy(sysCfg.domoticz_out_topic, (!strlen(webServer->arg("ot").c_str())) ? DOMOTICZ_OUT_TOPIC : webServer->arg("ot").c_str(), sizeof(sysCfg.domoticz_out_topic));
for (byte i = 0; i < 4; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1);
sysCfg.domoticz_relay_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str());
snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i +1);
sysCfg.domoticz_key_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str());
snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1);
sysCfg.domoticz_switch_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str());
}
for (byte i = 0; i < DOMOTICZ_MAX_SENSORS; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1);
sysCfg.domoticz_sensor_idx[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str());
}
sysCfg.domoticz_update_timer = (!strlen(webServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(webServer->arg("ut").c_str());
snprintf_P(log, sizeof(log), PSTR("HTTP: Domoticz in %s, out %s, idx %d, %d, %d, %d, update timer %d"),
sysCfg.domoticz_in_topic, sysCfg.domoticz_out_topic,
sysCfg.domoticz_relay_idx[0], sysCfg.domoticz_relay_idx[1], sysCfg.domoticz_relay_idx[2], sysCfg.domoticz_relay_idx[3],
sysCfg.domoticz_update_timer);
addLog(LOG_LEVEL_INFO, log);
snprintf_P(log, sizeof(log), PSTR("HTTP: key %d, %d, %d, %d, switch %d, %d, %d, %d, sensor %d, %d, %d, %d, %d"),
sysCfg.domoticz_key_idx[0], sysCfg.domoticz_key_idx[1], sysCfg.domoticz_key_idx[2], sysCfg.domoticz_key_idx[3],
sysCfg.domoticz_switch_idx[0], sysCfg.domoticz_switch_idx[1], sysCfg.domoticz_switch_idx[2], sysCfg.domoticz_switch_idx[3],
sysCfg.domoticz_sensor_idx[0], sysCfg.domoticz_sensor_idx[1], sysCfg.domoticz_sensor_idx[2], sysCfg.domoticz_sensor_idx[3], sysCfg.domoticz_sensor_idx[4]);
addLog(LOG_LEVEL_INFO, log);
}
#endif // USE_DOMOTICZ

233
sonoff/xdrv_wemohue.ino Normal file
View File

@ -0,0 +1,233 @@
/*
Copyright (c) 2017 Heiko Krupp and Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION)
#define UDP_BUFFER_SIZE 200 // Max UDP buffer size needed for M-SEARCH message
boolean udpConnected = false;
char packetBuffer[UDP_BUFFER_SIZE]; // buffer to hold incoming UDP packet
IPAddress ipMulticast(239, 255, 255, 250); // Simple Service Discovery Protocol (SSDP)
uint32_t portMulticast = 1900; // Multicast address and port
#ifdef USE_WEMO_EMULATION
/*********************************************************************************************\
* WeMo UPNP support routines
\*********************************************************************************************/
const char WEMO_MSEARCH[] PROGMEM =
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=86400\r\n"
"DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://{r1}:80/setup.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n"
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
"ST: urn:Belkin:device:**\r\n"
"USN: uuid:{r2}::urn:Belkin:device:**\r\n"
"X-User-Agent: redsonic\r\n"
"\r\n";
String wemo_serial()
{
char serial[15];
snprintf_P(serial, sizeof(serial), PSTR("201612K%07d"), ESP.getChipId());
return String(serial);
}
String wemo_UUID()
{
char uuid[26];
snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), wemo_serial().c_str());
return String(uuid);
}
void wemo_respondToMSearch()
{
char message[TOPSZ], log[LOGSZ];
if (portUDP.beginPacket(portUDP.remoteIP(), portUDP.remotePort())) {
String response = FPSTR(WEMO_MSEARCH);
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", wemo_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
snprintf_P(message, sizeof(message), PSTR("Response sent"));
} else {
snprintf_P(message, sizeof(message), PSTR("Failed to send response"));
}
snprintf_P(log, sizeof(log), PSTR("UPnP: Wemo %s to %s:%d"),
message, portUDP.remoteIP().toString().c_str(), portUDP.remotePort());
addLog(LOG_LEVEL_DEBUG, log);
}
#endif // USE_WEMO_EMULATION
#ifdef USE_HUE_EMULATION
/*********************************************************************************************\
* Hue Bridge UPNP support routines
* Need to send 3 response packets with varying ST and USN
\*********************************************************************************************/
const char HUE_RESPONSE[] PROGMEM =
"HTTP/1.0 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"EXT:\r\n"
"LOCATION: http://{r1}:80/description.xml\r\n"
"SERVER: FreeRTOS/7.4.2 UPnP/1.0 IpBridge/1.15.0\r\n"
"hue-bridgeid: {r2}\r\n";
const char HUE_ST1[] PROGMEM =
"ST: upnp:rootdevice\r\n";
const char HUE_USN1[] PROGMEM =
"USN: uuid:{r3}::upnp:rootdevice\r\n"
"\r\n";
const char HUE_ST2[] PROGMEM =
"ST: uuid:{r3}\r\n";
const char HUE_USN2[] PROGMEM =
"USN: uuid:{r3}\r\n"
"\r\n";
const char HUE_ST3[] PROGMEM =
"ST: urn:schemas-upnp-org:device:basic:1\r\n";
String hue_bridgeid()
{
char bridgeid[16];
snprintf_P(bridgeid, sizeof(bridgeid), PSTR("5CCF7FFFFE%03X"), ESP.getChipId());
return String(bridgeid);
}
String hue_UUID()
{
char serial[36];
snprintf_P(serial, sizeof(serial), PSTR("f6543a06-da50-11ba-8d8f-5ccf7f%03x"), ESP.getChipId());
return String(serial);
}
void hue_respondToMSearch()
{
char message[TOPSZ], log[LOGSZ];
if (portUDP.beginPacket(portUDP.remoteIP(), portUDP.remotePort())) {
String response = FPSTR(HUE_RESPONSE);
String response_st=FPSTR(HUE_ST1);
String response_usn=FPSTR(HUE_USN1);
response += response_st + response_usn;
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", hue_bridgeid());
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
snprintf_P(message, sizeof(message), PSTR("Response1 sent"));
// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
response = FPSTR(HUE_RESPONSE);
response_st=FPSTR(HUE_ST2);
response_usn=FPSTR(HUE_USN2);
response += response_st + response_usn;
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", hue_bridgeid());
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
snprintf_P(message, sizeof(message), PSTR("Response2 sent"));
// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
response = FPSTR(HUE_RESPONSE);
response_st=FPSTR(HUE_ST3);
response += response_st + response_usn;
response.replace("{r1}", WiFi.localIP().toString());
response.replace("{r2}", hue_bridgeid());
response.replace("{r3}", hue_UUID());
portUDP.write(response.c_str());
portUDP.endPacket();
snprintf_P(message, sizeof(message), PSTR("Response3 sent"));
// addLog(LOG_LEVEL_DEBUG_MORE, response.c_str());
} else {
snprintf_P(message, sizeof(message), PSTR("Failed to send response"));
}
snprintf_P(log, sizeof(log), PSTR("UPnP: HUE %s to %s:%d"),
message, portUDP.remoteIP().toString().c_str(), portUDP.remotePort());
addLog(LOG_LEVEL_DEBUG, log);
}
#endif // USE_HUE_EMULATION
/********************************************************************************************/
#if defined(USE_WEMO_EMULATION) || defined(USE_HUE_EMULATION)
boolean UDP_Disconnect()
{
if (udpConnected) {
WiFiUDP::stopAll();
addLog_P(LOG_LEVEL_DEBUG, PSTR("UPnP: Multicast disabled"));
udpConnected = false;
}
return udpConnected;
}
boolean UDP_Connect()
{
if (!udpConnected) {
if (portUDP.beginMulticast(WiFi.localIP(), ipMulticast, portMulticast)) {
addLog_P(LOG_LEVEL_INFO, PSTR("UPnP: Multicast (re)joined"));
udpConnected = true;
} else {
addLog_P(LOG_LEVEL_INFO, PSTR("UPnP: Multicast join failed"));
udpConnected = false;
}
}
return udpConnected;
}
void pollUDP()
{
if (udpConnected) {
if (portUDP.parsePacket()) {
int len = portUDP.read(packetBuffer, UDP_BUFFER_SIZE -1);
if (len > 0) packetBuffer[len] = 0;
String request = packetBuffer;
// addLog_P(LOG_LEVEL_DEBUG_MORE, packetBuffer);
if (request.indexOf("M-SEARCH") >= 0) {
#ifdef USE_WEMO_EMULATION
if (request.indexOf("urn:Belkin:device:**") > 0) {
wemo_respondToMSearch();
}
#endif // USE_WEMO_EMULATION
#ifdef USE_HUE_EMULATION
if (request.indexOf("ST: urn:schemas-upnp-org:device:basic:1") > 0 ||
request.indexOf("ST: upnp:rootdevice") > 0 ||
request.indexOf("ST: ssdp:all") > 0) {
hue_respondToMSearch();
}
#endif // USE_HUE_EMULATION
}
}
}
}
#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION
#endif // USE_WEMO_EMULATION || USE_HUE_EMULATION

465
sonoff/xdrv_ws2812.ino Normal file
View File

@ -0,0 +1,465 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_WS2812
/*********************************************************************************************\
* WS2812 Leds using NeopixelBus library
\*********************************************************************************************/
#include <NeoPixelBus.h>
#ifdef USE_WS2812_DMA
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> *strip = NULL;
#else
NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
#endif // USE_WS2812_DMA
#define COLOR_SATURATION 254.0f
struct wsColor {
uint8_t red, green, blue;
};
struct ColorScheme {
wsColor* colors;
uint8_t count;
};
wsColor incandescent[2] = { 255, 140, 20, 0, 0, 0 };
wsColor rgb[3] = { 255, 0, 0, 0, 255, 0, 0, 0, 255 };
wsColor christmas[2] = { 255, 0, 0, 0, 255, 0 };
wsColor hanukkah[2] = { 0, 0, 255, 255, 255, 255 };
wsColor kwanzaa[3] = { 255, 0, 0, 0, 0, 0, 0, 255, 0 };
wsColor rainbow[7] = { 255, 0, 0, 255, 128, 0, 255, 255, 0, 0, 255, 0, 0, 0, 255, 128, 0, 255, 255, 0, 255 };
wsColor fire[3] = { 255, 0, 0, 255, 102, 0, 255, 192, 0 };
ColorScheme schemes[7] = {
incandescent, 2,
rgb, 3,
christmas, 2,
hanukkah, 2,
kwanzaa, 3,
rainbow, 7,
fire, 3 };
uint8_t widthValues[5] = {
1, // Small
2, // Medium
4, // Large
8, // Largest
255 }; // All
uint8_t repeatValues[5] = {
8, // Small
6, // Medium
4, // Large
2, // Largest
1 }; // All
uint8_t speedValues[6] = {
0, // None
18, // Slowest
14, // Slower
10, // Slow
6, // Fast
2 }; // Fastest
uint8_t ledTable[] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4,
4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8,
8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14,
14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22,
22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32,
33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45,
46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78,
80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99,
101,102,104,105,107,108,110,111,113,114,116,117,119,121,122,124,
125,127,129,130,132,134,135,137,139,141,142,144,146,148,150,151,
153,155,157,159,161,163,165,166,168,170,172,174,176,178,180,182,
184,186,189,191,193,195,197,199,201,204,206,208,210,212,215,217,
219,221,224,226,228,231,233,235,238,240,243,245,248,250,253,255 };
uint8_t lany = 0;
RgbColor dcolor, tcolor, lcolor;
uint8_t wakeupDimmer = 0;
uint16_t wakeupCntr = 0;
unsigned long stripTimerCntr = 0; // Bars and Gradient
void ws2812_setDim(uint8_t myDimmer)
{
float newDim = 100 / (float)myDimmer;
float fmyRed = (float)sysCfg.ws_red / newDim;
float fmyGrn = (float)sysCfg.ws_green / newDim;
float fmyBlu = (float)sysCfg.ws_blue / newDim;
dcolor.R = (uint8_t)fmyRed;
dcolor.G = (uint8_t)fmyGrn;
dcolor.B = (uint8_t)fmyBlu;
}
void ws2812_setColor(uint16_t led, char* colstr)
{
HtmlColor hcolor;
char log[LOGSZ], lcolstr[8];
snprintf_P(lcolstr, sizeof(lcolstr), PSTR("#%s"), colstr);
uint8_t result = hcolor.Parse<HtmlColorNames>((char *)lcolstr, 7);
if (result) {
if (led) {
strip->SetPixelColor(led -1, RgbColor(hcolor)); // Led 1 is strip Led 0 -> substract offset 1
strip->Show();
} else {
dcolor = RgbColor(hcolor);
// snprintf_P(log, sizeof(log), PSTR("DBG: Red %02X, Green %02X, Blue %02X"), dcolor.R, dcolor.G, dcolor.B);
// addLog(LOG_LEVEL_DEBUG, log);
uint16_t temp = dcolor.R;
if (temp < dcolor.G) temp = dcolor.G;
if (temp < dcolor.B) temp = dcolor.B;
float mDim = (float)temp / 2.55;
sysCfg.ws_dimmer = (uint8_t)mDim;
float newDim = 100 / mDim;
float fmyRed = (float)dcolor.R * newDim;
float fmyGrn = (float)dcolor.G * newDim;
float fmyBlu = (float)dcolor.B * newDim;
sysCfg.ws_red = (uint8_t)fmyRed;
sysCfg.ws_green = (uint8_t)fmyGrn;
sysCfg.ws_blue = (uint8_t)fmyBlu;
lany = 1;
}
}
}
void ws2812_replaceHSB(String *response)
{
ws2812_setDim(sysCfg.ws_dimmer);
HsbColor hsb=HsbColor(dcolor);
response->replace("{h}", String((uint16_t)(65535.0f * hsb.H)));
response->replace("{s}", String((uint8_t)(COLOR_SATURATION * hsb.S)));
response->replace("{b}", String((uint8_t)(COLOR_SATURATION * hsb.B)));
}
void ws2812_changeBrightness(uint8_t bri)
{
char rgb[7];
//sysCfg.ws_ledtable=1; // Switch on Gamma Correction for "natural" brightness controll
ws2812_setDim(sysCfg.ws_dimmer);
HsbColor hsb = HsbColor(dcolor);
if (!bri) bri=1;
if (bri==255) bri=252;
hsb.B=(float)(bri/COLOR_SATURATION);
RgbColor tmp = RgbColor(hsb);
sprintf(rgb,"%02X%02X%02X", tmp.R, tmp.G, tmp.B);
ws2812_setColor(0,rgb);
}
void ws2812_getColor(uint16_t led, char* svalue, uint16_t ssvalue)
{
RgbColor mcolor;
char stemp[20];
if (led) {
mcolor = strip->GetPixelColor(led -1);
snprintf_P(stemp, sizeof(stemp), PSTR("Led%d"), led);
} else {
ws2812_setDim(sysCfg.ws_dimmer);
mcolor = dcolor;
snprintf_P(stemp, sizeof(stemp), PSTR("Color"));
}
uint32_t color = (uint32_t)mcolor.R << 16;
color += (uint32_t)mcolor.G << 8;
color += (uint32_t)mcolor.B;
snprintf_P(svalue, ssvalue, PSTR("{\"%s\":\"%06X\"}"), stemp, color);
}
void ws2812_stripShow()
{
RgbColor c;
if (sysCfg.ws_ledtable) {
for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) {
c = strip->GetPixelColor(i);
strip->SetPixelColor(i, RgbColor(ledTable[c.R], ledTable[c.G], ledTable[c.B]));
}
}
strip->Show();
}
void ws2812_resetWakupState()
{
wakeupDimmer = 0;
wakeupCntr = 0;
}
void ws2812_resetStripTimer()
{
stripTimerCntr = 0;
}
int mod(int a, int b)
{
int ret = a % b;
if (ret < 0) ret += b;
return ret;
}
void ws2812_clock()
{
RgbColor c;
strip->ClearTo(0); // Reset strip
float newDim = 100 / (float)sysCfg.ws_dimmer;
float f1 = 255 / newDim;
uint8_t i1 = (uint8_t)f1;
float f2 = 127 / newDim;
uint8_t i2 = (uint8_t)f2;
float f3 = 63 / newDim;
uint8_t i3 = (uint8_t)f3;
int j = sysCfg.ws_pixels;
int clksize = 600 / j;
int i = (rtcTime.Second * 10) / clksize;
c = strip->GetPixelColor(mod(i, j)); c.B = i1; strip->SetPixelColor(mod(i, j), c);
i = (rtcTime.Minute * 10) / clksize;
c = strip->GetPixelColor(mod(i -1, j)); c.G = i3; strip->SetPixelColor(mod(i -1, j), c);
c = strip->GetPixelColor(mod(i, j)); c.G = i1; strip->SetPixelColor(mod(i, j), c);
c = strip->GetPixelColor(mod(i +1, j)); c.G = i3; strip->SetPixelColor(mod(i +1, j), c);
i = (rtcTime.Hour % 12) * (50 / clksize);
c = strip->GetPixelColor(mod(i -2, j)); c.R = i3; strip->SetPixelColor(mod(i -2, j), c);
c = strip->GetPixelColor(mod(i -1, j)); c.R = i2; strip->SetPixelColor(mod(i -1, j), c);
c = strip->GetPixelColor(mod(i, j)); c.R = i1; strip->SetPixelColor(mod(i, j), c);
c = strip->GetPixelColor(mod(i +1, j)); c.R = i2; strip->SetPixelColor(mod(i +1, j), c);
c = strip->GetPixelColor(mod(i +2, j)); c.R = i3; strip->SetPixelColor(mod(i +2, j), c);
ws2812_stripShow();
}
void ws2812_gradientColor(struct wsColor* mColor, uint8_t range, uint8_t gradRange, uint8_t i)
{
/*
* Compute the color of a pixel at position i using a gradient of the color scheme.
* This function is used internally by the gradient function.
*/
ColorScheme scheme = schemes[sysCfg.ws_scheme -3];
uint8_t curRange = i / range;
uint8_t rangeIndex = i % range;
uint8_t colorIndex = rangeIndex / gradRange;
uint8_t start = colorIndex;
uint8_t end = colorIndex +1;
if (curRange % 2 != 0) {
start = (scheme.count -1) - start;
end = (scheme.count -1) - end;
}
float newDim = 100 / (float)sysCfg.ws_dimmer;
float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / newDim;
float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / newDim;
float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / newDim;
mColor->red = (uint8_t)fmyRed;
mColor->green = (uint8_t)fmyGrn;
mColor->blue = (uint8_t)fmyBlu;
}
void ws2812_gradient()
{
/*
* This routine courtesy Tony DiCola (Adafruit)
* Display a gradient of colors for the current color scheme.
* Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient).
*/
RgbColor c;
ColorScheme scheme = schemes[sysCfg.ws_scheme -3];
if (scheme.count < 2) return;
uint8_t repeat = repeatValues[sysCfg.ws_width]; // number of scheme.count per ledcount
uint8_t range = (uint8_t)ceil((float)sysCfg.ws_pixels / (float)repeat);
uint8_t gradRange = (uint8_t)ceil((float)range / (float)(scheme.count - 1));
uint8_t offset = speedValues[sysCfg.ws_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.ws_speed] : 0;
wsColor oldColor, currentColor;
ws2812_gradientColor(&oldColor, range, gradRange, offset);
currentColor = oldColor;
for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) {
if (repeatValues[sysCfg.ws_width] > 1) ws2812_gradientColor(&currentColor, range, gradRange, i +offset);
if (sysCfg.ws_speed > 0) {
// Blend old and current color based on time for smooth movement.
c.R = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.red, currentColor.red);
c.G = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.green, currentColor.green);
c.B = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.blue, currentColor.blue);
}
else {
// No animation, just use the current color.
c.R = currentColor.red;
c.G = currentColor.green;
c.B = currentColor.blue;
}
strip->SetPixelColor(i, c);
oldColor = currentColor;
}
ws2812_stripShow();
}
void ws2812_bars()
{
/*
* This routine courtesy Tony DiCola (Adafruit)
* Display solid bars of color for the current color scheme.
* Width is the width of each bar in pixels/lights.
*/
RgbColor c;
uint16_t i;
ColorScheme scheme = schemes[sysCfg.ws_scheme -3];
uint8_t maxSize = sysCfg.ws_pixels / scheme.count;
if (widthValues[sysCfg.ws_width] > maxSize) maxSize = 0;
uint8_t offset = speedValues[sysCfg.ws_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.ws_speed] : 0;
wsColor mcolor[scheme.count];
memcpy(mcolor, scheme.colors, sizeof(mcolor));
float newDim = 100 / (float)sysCfg.ws_dimmer;
for (i = 0; i < scheme.count; i++) {
float fmyRed = (float)mcolor[i].red / newDim;
float fmyGrn = (float)mcolor[i].green / newDim;
float fmyBlu = (float)mcolor[i].blue / newDim;
mcolor[i].red = (uint8_t)fmyRed;
mcolor[i].green = (uint8_t)fmyGrn;
mcolor[i].blue = (uint8_t)fmyBlu;
}
uint8_t colorIndex = offset % scheme.count;
for (i = 0; i < sysCfg.ws_pixels; i++) {
if (maxSize)
colorIndex = ((i + offset) % (scheme.count * widthValues[sysCfg.ws_width])) / widthValues[sysCfg.ws_width];
c.R = mcolor[colorIndex].red;
c.G = mcolor[colorIndex].green;
c.B = mcolor[colorIndex].blue;
strip->SetPixelColor(i, c);
}
ws2812_stripShow();
}
void ws2812_animate()
{
char log[LOGSZ];
uint8_t fadeValue;
stripTimerCntr++;
if (power == 0) { // Power Off
stripTimerCntr = 0;
tcolor = 0;
}
else {
switch (sysCfg.ws_scheme) {
case 0: // Power On
ws2812_setDim(sysCfg.ws_dimmer);
if (sysCfg.ws_fade == 0) {
tcolor = dcolor;
} else {
if (tcolor != dcolor) {
if (tcolor.R < dcolor.R) tcolor.R += ((dcolor.R - tcolor.R) >> sysCfg.ws_speed) +1;
if (tcolor.G < dcolor.G) tcolor.G += ((dcolor.G - tcolor.G) >> sysCfg.ws_speed) +1;
if (tcolor.B < dcolor.B) tcolor.B += ((dcolor.B - tcolor.B) >> sysCfg.ws_speed) +1;
if (tcolor.R > dcolor.R) tcolor.R -= ((tcolor.R - dcolor.R) >> sysCfg.ws_speed) +1;
if (tcolor.G > dcolor.G) tcolor.G -= ((tcolor.G - dcolor.G) >> sysCfg.ws_speed) +1;
if (tcolor.B > dcolor.B) tcolor.B -= ((tcolor.B - dcolor.B) >> sysCfg.ws_speed) +1;
}
}
break;
case 1: // Wake up light
wakeupCntr++;
if (wakeupDimmer == 0) {
tcolor = 0;
wakeupDimmer++;
}
else {
if (wakeupCntr > ((sysCfg.ws_wakeup * STATES) / sysCfg.ws_dimmer)) {
wakeupCntr = 0;
wakeupDimmer++;
if (wakeupDimmer <= sysCfg.ws_dimmer) {
ws2812_setDim(wakeupDimmer);
tcolor = dcolor;
} else
sysCfg.ws_scheme = 0;
}
}
break;
case 2: // Clock
if ((state == (STATES/10)*2) || (lany != 2)) ws2812_clock();
lany = 2;
break;
default:
if (sysCfg.ws_fade == 1) ws2812_gradient(); else ws2812_bars();
lany = 1;
break;
}
}
if ((sysCfg.ws_scheme <= 1) || (!(power &1))) {
if ((lcolor != tcolor) || lany) {
lany = 0;
lcolor = tcolor;
// snprintf_P(log, sizeof(log), PSTR("DBG: StripPixels %d, CfgPixels %d, Red %02X, Green %02X, Blue %02X"), strip->PixelCount(), sysCfg.ws_pixels, lcolor.R, lcolor.G, lcolor.B);
// addLog(LOG_LEVEL_DEBUG, log);
if (sysCfg.ws_ledtable) {
for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) strip->SetPixelColor(i, RgbColor(ledTable[lcolor.R],ledTable[lcolor.G],ledTable[lcolor.B]));
} else {
for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) strip->SetPixelColor(i, lcolor);
}
strip->Show();
}
}
}
void ws2812_update()
{
lany = 1;
}
void ws2812_pixels()
{
strip->ClearTo(0);
strip->Show();
tcolor = 0;
lany = 1;
}
void ws2812_init()
{
#ifdef USE_WS2812_DMA
strip = new NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
#else
strip = new NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
#endif // USE_WS2812_DMA
strip->Begin();
ws2812_pixels();
}
#endif // USE_WS2812

110
sonoff/xsns_bh1750.ino Normal file
View File

@ -0,0 +1,110 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_I2C
#ifdef USE_BH1750
/*********************************************************************************************\
* BH1750 - Ambient Light Intensity
\*********************************************************************************************/
#define BH1750_ADDR1 0x23
#define BH1750_ADDR2 0x5C
#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 // Start measurement at 1lx resolution. Measurement time is approx 120ms.
uint8_t bh1750addr, bh1750type = 0;
char bh1750stype[7];
uint16_t bh1750_readLux(void)
{
Wire.requestFrom(bh1750addr, (uint8_t)2);
byte msb = Wire.read();
byte lsb = Wire.read();
uint16_t value = ((msb << 8) | lsb) / 1.2;
return value;
}
boolean bh1750_detect()
{
if (bh1750type) return true;
char log[LOGSZ];
uint8_t status;
boolean success = false;
bh1750addr = BH1750_ADDR1;
Wire.beginTransmission(bh1750addr);
Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE);
status = Wire.endTransmission();
if (status) {
bh1750addr = BH1750_ADDR2;
Wire.beginTransmission(bh1750addr);
Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE);
status = Wire.endTransmission();
}
if (!status) {
success = true;
bh1750type = 1;
snprintf_P(bh1750stype, sizeof(bh1750stype), PSTR("BH1750"));
}
if (success) {
snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), bh1750stype, bh1750addr);
addLog(LOG_LEVEL_DEBUG, log);
} else {
bh1750type = 0;
}
return success;
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void bh1750_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
if (!bh1750type) return;
uint16_t l = bh1750_readLux();
snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Illuminance\":%d}"), svalue, bh1750stype, l);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor5(l);
#endif // USE_DOMOTICZ
}
#ifdef USE_WEBSERVER
String bh1750_webPresent()
{
String page = "";
if (bh1750type) {
uint16_t l = bh1750_readLux();
page += F("<tr><td>Illuminance: </td><td>"); page += String(l); page += F(" lx</td></tr>");
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_BH1750
#endif // USE_I2C

477
sonoff/xsns_bmp.ino Normal file
View File

@ -0,0 +1,477 @@
/*
Copyright (c) 2017 Heiko Krupp and Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_I2C
#ifdef USE_BMP
/*********************************************************************************************\
* BMP085, BMP180, BMP280, BME280 - Pressure and Temperature and Humidy (BME280 only)
*
* Source: Heiko Krupp and Adafruit Industries
\*********************************************************************************************/
#define BMP_ADDR 0x77
#define BMP180_CHIPID 0x55
#define BMP280_CHIPID 0x58
#define BME280_CHIPID 0x60
#define BMP_REGISTER_CHIPID 0xD0
uint8_t bmpaddr, bmptype = 0;
char bmpstype[7];
/*********************************************************************************************\
* BMP085 and BME180
*
* Programmer : Heiko Krupp with changes from Theo Arends
\*********************************************************************************************/
#define BMP180_REG_CONTROL 0xF4
#define BMP180_REG_RESULT 0xF6
#define BMP180_TEMPERATURE 0x2E
#define BMP180_PRESSURE3 0xF4 // Max. oversampling -> OSS = 3
#define BMP180_AC1 0xAA
#define BMP180_AC2 0xAC
#define BMP180_AC3 0xAE
#define BMP180_AC4 0xB0
#define BMP180_AC5 0xB2
#define BMP180_AC6 0xB4
#define BMP180_VB1 0xB6
#define BMP180_VB2 0xB8
#define BMP180_MB 0xBA
#define BMP180_MC 0xBC
#define BMP180_MD 0xBE
#define BMP180_OSS 3
int16_t cal_ac1,cal_ac2,cal_ac3,cal_b1,cal_b2,cal_mc,cal_md;
uint16_t cal_ac4,cal_ac5,cal_ac6;
int32_t bmp180_b5 = 0;
boolean bmp180_calibration()
{
cal_ac1 = i2c_read16(bmpaddr, BMP180_AC1);
cal_ac2 = i2c_read16(bmpaddr, BMP180_AC2);
cal_ac3 = i2c_read16(bmpaddr, BMP180_AC3);
cal_ac4 = i2c_read16(bmpaddr, BMP180_AC4);
cal_ac5 = i2c_read16(bmpaddr, BMP180_AC5);
cal_ac6 = i2c_read16(bmpaddr, BMP180_AC6);
cal_b1 = i2c_read16(bmpaddr, BMP180_VB1);
cal_b2 = i2c_read16(bmpaddr, BMP180_VB2);
cal_mc = i2c_read16(bmpaddr, BMP180_MC);
cal_md = i2c_read16(bmpaddr, BMP180_MD);
// Check for Errors in calibration data. Value never is 0x0000 or 0xFFFF
if(!cal_ac1 | !cal_ac2 | !cal_ac3 | !cal_ac4 | !cal_ac5 |
!cal_ac6 | !cal_b1 | !cal_b2 | !cal_mc | !cal_md)
return false;
if((cal_ac1==0xFFFF)|
(cal_ac2==0xFFFF)|
(cal_ac3==0xFFFF)|
(cal_ac4==0xFFFF)|
(cal_ac5==0xFFFF)|
(cal_ac6==0xFFFF)|
(cal_b1==0xFFFF)|
(cal_b2==0xFFFF)|
(cal_mc==0xFFFF)|
(cal_md==0xFFFF))
return false;
return true;
}
double bmp180_readTemperature()
{
i2c_write8(bmpaddr, BMP180_REG_CONTROL, BMP180_TEMPERATURE);
delay(5); // 5ms conversion time
int ut = i2c_read16(bmpaddr, BMP180_REG_RESULT);
int32_t x1 = (ut - (int32_t)cal_ac6) * ((int32_t)cal_ac5) >> 15;
int32_t x2 = ((int32_t)cal_mc << 11) / (x1+(int32_t)cal_md);
bmp180_b5=x1+x2;
return ((bmp180_b5+8)>>4)/10.0;
}
double bmp180_readPressure()
{
int32_t p;
uint8_t msb,lsb,xlsb;
i2c_write8(bmpaddr, BMP180_REG_CONTROL, BMP180_PRESSURE3); // Highest resolution
delay(2 + (4 << BMP180_OSS)); // 26ms conversion time at ultra high resolution
uint32_t up = i2c_read24(bmpaddr, BMP180_REG_RESULT);
up >>= (8 - BMP180_OSS);
int32_t b6 = bmp180_b5 - 4000;
int32_t x1 = ((int32_t)cal_b2 * ( (b6 * b6)>>12 )) >> 11;
int32_t x2 = ((int32_t)cal_ac2 * b6) >> 11;
int32_t x3 = x1 + x2;
int32_t b3 = ((((int32_t)cal_ac1*4 + x3) << BMP180_OSS) + 2)>>2;
x1 = ((int32_t)cal_ac3 * b6) >> 13;
x2 = ((int32_t)cal_b1 * ((b6 * b6) >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
uint32_t b4 = ((uint32_t)cal_ac4 * (uint32_t)(x3 + 32768)) >> 15;
uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)( 50000UL >> BMP180_OSS);
if (b7 < 0x80000000) {
p = (b7 * 2) / b4;
} else {
p = (b7 / b4) * 2;
}
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
p += ((x1 + x2 + (int32_t)3791)>>4);
return p/100.0; // convert to mbar
}
double bmp180_calcSealevelPressure(float pAbs, float altitude_meters)
{
double pressure = pAbs*100.0;
return (double)(pressure / pow(1.0-altitude_meters/44330, 5.255))/100.0;
}
/*********************************************************************************************\
* BMP280 and BME280
*
* Programmer : BMP280/BME280 Datasheet and Adafruit with changes by Theo Arends
\*********************************************************************************************/
#define BME280_REGISTER_CONTROLHUMID 0xF2
#define BME280_REGISTER_CONTROL 0xF4
#define BME280_REGISTER_PRESSUREDATA 0xF7
#define BME280_REGISTER_TEMPDATA 0xFA
#define BME280_REGISTER_HUMIDDATA 0xFD
#define BME280_REGISTER_DIG_T1 0x88
#define BME280_REGISTER_DIG_T2 0x8A
#define BME280_REGISTER_DIG_T3 0x8C
#define BME280_REGISTER_DIG_P1 0x8E
#define BME280_REGISTER_DIG_P2 0x90
#define BME280_REGISTER_DIG_P3 0x92
#define BME280_REGISTER_DIG_P4 0x94
#define BME280_REGISTER_DIG_P5 0x96
#define BME280_REGISTER_DIG_P6 0x98
#define BME280_REGISTER_DIG_P7 0x9A
#define BME280_REGISTER_DIG_P8 0x9C
#define BME280_REGISTER_DIG_P9 0x9E
#define BME280_REGISTER_DIG_H1 0xA1
#define BME280_REGISTER_DIG_H2 0xE1
#define BME280_REGISTER_DIG_H3 0xE3
#define BME280_REGISTER_DIG_H4 0xE4
#define BME280_REGISTER_DIG_H5 0xE5
#define BME280_REGISTER_DIG_H6 0xE7
struct bme280_calib_data
{
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
uint8_t dig_H1;
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
} _bme280_calib;
int32_t t_fine;
boolean bmp280_calibrate()
{
// if (i2c_read8(bmpaddr, BMP_REGISTER_CHIPID) != BMP280_CHIPID) return false;
_bme280_calib.dig_T1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_T1);
_bme280_calib.dig_T2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T2);
_bme280_calib.dig_T3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T3);
_bme280_calib.dig_P1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_P1);
_bme280_calib.dig_P2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P2);
_bme280_calib.dig_P3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P3);
_bme280_calib.dig_P4 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P4);
_bme280_calib.dig_P5 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P5);
_bme280_calib.dig_P6 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P6);
_bme280_calib.dig_P7 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P7);
_bme280_calib.dig_P8 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P8);
_bme280_calib.dig_P9 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P9);
// i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0x3F); // Temp 1x oversampling, Press 16x oversampling, normal mode (Adafruit)
i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit)
return true;
}
boolean bme280_calibrate()
{
// if (i2c_read8(bmpaddr, BMP_REGISTER_CHIPID) != BME280_CHIPID) return false;
_bme280_calib.dig_T1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_T1);
_bme280_calib.dig_T2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T2);
_bme280_calib.dig_T3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_T3);
_bme280_calib.dig_P1 = i2c_read16_LE(bmpaddr, BME280_REGISTER_DIG_P1);
_bme280_calib.dig_P2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P2);
_bme280_calib.dig_P3 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P3);
_bme280_calib.dig_P4 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P4);
_bme280_calib.dig_P5 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P5);
_bme280_calib.dig_P6 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P6);
_bme280_calib.dig_P7 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P7);
_bme280_calib.dig_P8 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P8);
_bme280_calib.dig_P9 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_P9);
_bme280_calib.dig_H1 = i2c_read8(bmpaddr, BME280_REGISTER_DIG_H1);
_bme280_calib.dig_H2 = i2c_readS16_LE(bmpaddr, BME280_REGISTER_DIG_H2);
_bme280_calib.dig_H3 = i2c_read8(bmpaddr, BME280_REGISTER_DIG_H3);
_bme280_calib.dig_H4 = (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H4) << 4) | (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H4 + 1) & 0xF);
_bme280_calib.dig_H5 = (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H5 + 1) << 4) | (i2c_read8(bmpaddr, BME280_REGISTER_DIG_H5) >> 4);
_bme280_calib.dig_H6 = (int8_t)i2c_read8(bmpaddr, BME280_REGISTER_DIG_H6);
// Set before CONTROL_meas (DS 5.4.3)
i2c_write8(bmpaddr, BME280_REGISTER_CONTROLHUMID, 0x05); // 16x oversampling (Adafruit)
i2c_write8(bmpaddr, BME280_REGISTER_CONTROL, 0xB7); // 16x oversampling, normal mode (Adafruit)
return true;
}
double bmp280_readTemperature(void)
{
int32_t var1, var2;
int32_t adc_T = i2c_read24(bmpaddr, BME280_REGISTER_TEMPDATA);
adc_T >>= 4;
var1 = ((((adc_T>>3) - ((int32_t)_bme280_calib.dig_T1 <<1))) * ((int32_t)_bme280_calib.dig_T2)) >> 11;
var2 = (((((adc_T>>4) - ((int32_t)_bme280_calib.dig_T1)) * ((adc_T>>4) - ((int32_t)_bme280_calib.dig_T1))) >> 12) *
((int32_t)_bme280_calib.dig_T3)) >> 14;
t_fine = var1 + var2;
double T = (t_fine * 5 + 128) >> 8;
return T / 100.0;
}
double bmp280_readPressure(void)
{
int64_t var1, var2, p;
// Must be done first to get the t_fine variable set up
// bmp280_readTemperature();
int32_t adc_P = i2c_read24(bmpaddr, BME280_REGISTER_PRESSUREDATA);
adc_P >>= 4;
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)_bme280_calib.dig_P6;
var2 = var2 + ((var1 * (int64_t)_bme280_calib.dig_P5) << 17);
var2 = var2 + (((int64_t)_bme280_calib.dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)_bme280_calib.dig_P3) >> 8) + ((var1 * (int64_t)_bme280_calib.dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)_bme280_calib.dig_P1) >> 33;
if (var1 == 0) {
return 0; // avoid exception caused by division by zero
}
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)_bme280_calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)_bme280_calib.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)_bme280_calib.dig_P7) << 4);
return (double)p / 25600.0;
}
double bme280_readHumidity(void)
{
int32_t v_x1_u32r;
// Must be done first to get the t_fine variable set up
// bmp280_readTemperature();
int32_t adc_H = i2c_read16(bmpaddr, BME280_REGISTER_HUMIDDATA);
v_x1_u32r = (t_fine - ((int32_t)76800));
v_x1_u32r = (((((adc_H << 14) - (((int32_t)_bme280_calib.dig_H4) << 20) -
(((int32_t)_bme280_calib.dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) *
(((((((v_x1_u32r * ((int32_t)_bme280_calib.dig_H6)) >> 10) *
(((v_x1_u32r * ((int32_t)_bme280_calib.dig_H3)) >> 11) + ((int32_t)32768))) >> 10) +
((int32_t)2097152)) * ((int32_t)_bme280_calib.dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
((int32_t)_bme280_calib.dig_H1)) >> 4));
v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r;
v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r;
double h = (v_x1_u32r >> 12);
return h / 1024.0;
}
/*********************************************************************************************\
* BMP
\*********************************************************************************************/
double bmp_convertCtoF(double c)
{
return c * 1.8 + 32;
}
double bmp_readTemperature(bool S)
{
double t = NAN;
switch (bmptype) {
case BMP180_CHIPID:
t = bmp180_readTemperature();
break;
case BMP280_CHIPID:
case BME280_CHIPID:
t = bmp280_readTemperature();
}
if (!isnan(t)) {
if(S) t = bmp_convertCtoF(t);
return t;
}
return 0;
}
double bmp_readPressure(void)
{
switch (bmptype) {
case BMP180_CHIPID:
return bmp180_readPressure();
case BMP280_CHIPID:
case BME280_CHIPID:
return bmp280_readPressure();
}
return 0;
}
double bmp_readHumidity(void)
{
switch (bmptype) {
case BMP180_CHIPID:
case BMP280_CHIPID:
break;
case BME280_CHIPID:
return bme280_readHumidity();
}
return 0;
}
boolean bmp_detect()
{
if (bmptype) return true;
char log[LOGSZ];
boolean success = false;
bmpaddr = BMP_ADDR;
bmptype = i2c_read8(bmpaddr, BMP_REGISTER_CHIPID);
if (!bmptype) {
bmpaddr--;
bmptype = i2c_read8(bmpaddr, BMP_REGISTER_CHIPID);
}
snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BMP"));
switch (bmptype) {
case BMP180_CHIPID:
success = bmp180_calibration();
snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BMP180"));
break;
case BMP280_CHIPID:
success = bmp280_calibrate();
snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BMP280"));
break;
case BME280_CHIPID:
success = bme280_calibrate();
snprintf_P(bmpstype, sizeof(bmpstype), PSTR("BME280"));
}
if (success) {
snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), bmpstype, bmpaddr);
addLog(LOG_LEVEL_DEBUG, log);
} else {
bmptype = 0;
}
return success;
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void bmp_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
if (!bmptype) return;
char stemp1[10], stemp2[10], stemp3[10];
double t = bmp_readTemperature(TEMP_CONVERSION);
double p = bmp_readPressure();
double h = bmp_readHumidity();
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
dtostrf(p, 1, PRESSURE_RESOLUTION &3, stemp2);
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp3);
if (!strcmp(bmpstype,"BME280")) {
snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\", \"Pressure\":\"%s\"}"),
svalue, bmpstype, stemp1, stemp3, stemp2);
} else {
snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":\"%s\", \"Pressure\":\"%s\"}"),
svalue, bmpstype, stemp1, stemp2);
}
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor3(stemp1, stemp3, stemp2);
#endif // USE_DOMOTICZ
}
#ifdef USE_WEBSERVER
String bmp_webPresent()
{
String page = "";
if (bmptype) {
char itemp[10], iconv[10];
snprintf_P(iconv, sizeof(iconv), PSTR("&deg;%c"), (TEMP_CONVERSION) ? 'F' : 'C');
double t_bmp = bmp_readTemperature(TEMP_CONVERSION);
double p_bmp = bmp_readPressure();
double h_bmp = bmp_readHumidity();
dtostrf(t_bmp, 1, TEMP_RESOLUTION &3, itemp);
page += F("<tr><td>BMP Temperature: </td><td>"); page += itemp; page += iconv; page += F("</td></tr>");
if (!strcmp(bmpstype,"BME280")) {
dtostrf(h_bmp, 1, HUMIDITY_RESOLUTION &3, itemp);
page += F("<tr><td>BMP Humidity: </td><td>"); page += itemp; page += F("%</td></tr>");
}
dtostrf(p_bmp, 1, PRESSURE_RESOLUTION &3, itemp);
page += F("<tr><td>BMP Pressure: </td><td>"); page += itemp; page += F(" hPa</td></tr>");
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_BMP
#endif // USE_I2C

214
sonoff/xsns_dht.ino Normal file
View File

@ -0,0 +1,214 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_DHT
/*********************************************************************************************\
* DHT11, DHT21 (AM2301), DHT22 (AM2302, AM2321) - Temperature and Humidy
*
* Reading temperature or humidity takes about 250 milliseconds!
* Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
* Source: Adafruit Industries https://github.com/adafruit/DHT-sensor-library
\*********************************************************************************************/
#define MIN_INTERVAL 2000
uint8_t data[5];
uint32_t _lastreadtime, _maxcycles;
bool _lastresult;
float mt, mh = 0;
void dht_readPrep()
{
digitalWrite(pin[GPIO_DHT11], HIGH);
}
uint32_t dht_expectPulse(bool level)
{
uint32_t count = 0;
while (digitalRead(pin[GPIO_DHT11]) == level)
if (count++ >= _maxcycles) return 0;
return count;
}
boolean dht_read()
{
char log[LOGSZ];
uint32_t cycles[80];
uint32_t currenttime = millis();
if ((currenttime - _lastreadtime) < 2000) {
return _lastresult;
}
_lastreadtime = currenttime;
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
// digitalWrite(pin[GPIO_DHT11], HIGH);
// delay(250);
pinMode(pin[GPIO_DHT11], OUTPUT);
digitalWrite(pin[GPIO_DHT11], LOW);
delay(20);
noInterrupts();
digitalWrite(pin[GPIO_DHT11], HIGH);
delayMicroseconds(40);
pinMode(pin[GPIO_DHT11], INPUT_PULLUP);
delayMicroseconds(10);
if (dht_expectPulse(LOW) == 0) {
addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for start signal low pulse"));
_lastresult = false;
return _lastresult;
}
if (dht_expectPulse(HIGH) == 0) {
addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for start signal high pulse"));
_lastresult = false;
return _lastresult;
}
for (int i=0; i<80; i+=2) {
cycles[i] = dht_expectPulse(LOW);
cycles[i+1] = dht_expectPulse(HIGH);
}
interrupts();
for (int i=0; i<40; ++i) {
uint32_t lowCycles = cycles[2*i];
uint32_t highCycles = cycles[2*i+1];
if ((lowCycles == 0) || (highCycles == 0)) {
addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Timeout waiting for pulse"));
_lastresult = false;
return _lastresult;
}
data[i/8] <<= 1;
if (highCycles > lowCycles) data[i/8] |= 1;
}
snprintf_P(log, sizeof(log), PSTR("DHT: Received %02X, %02X, %02X, %02X, %02X =? %02X"),
data[0], data[1], data[2], data[3], data[4], (data[0] + data[1] + data[2] + data[3]) & 0xFF);
addLog(LOG_LEVEL_DEBUG, log);
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
_lastresult = true;
return _lastresult;
} else {
addLog_P(LOG_LEVEL_DEBUG, PSTR("DHT: Checksum failure"));
_lastresult = false;
return _lastresult;
}
}
float dht_convertCtoF(float c)
{
return c * 1.8 + 32;
}
boolean dht_readTempHum(bool S, float &t, float &h)
{
if (!mh) {
t = NAN;
h = NAN;
} else {
t = mt;
h = mh;
}
if (dht_read()) {
switch (dht_type) {
case DHT11:
h = data[0];
t = data[2];
if(S) t = dht_convertCtoF(t);
break;
case DHT22:
case DHT21:
h = data[0];
h *= 256;
h += data[1];
h *= 0.1;
t = data[2] & 0x7F;
t *= 256;
t += data[3];
t *= 0.1;
if (data[2] & 0x80) t *= -1;
if(S) t = dht_convertCtoF(t);
break;
}
if (!isnan(t)) mt = t;
if (!isnan(h)) mh = h;
}
return (!isnan(t) && !isnan(h));
}
void dht_init()
{
char log[LOGSZ];
_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for
// reading pulses from DHT sensor.
pinMode(pin[GPIO_DHT11], INPUT_PULLUP);
_lastreadtime = -MIN_INTERVAL;
snprintf_P(log, sizeof(log), PSTR("DHT: Max clock cycles %d"), _maxcycles);
addLog(LOG_LEVEL_DEBUG, log);
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void dht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
char stemp1[10], stemp2[10];
float t, h;
if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2);
snprintf_P(svalue, ssvalue, PSTR("%s, \"DHT\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\"}"), svalue, stemp1, stemp2);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor2(stemp1, stemp2);
#endif // USE_DOMOTICZ
}
}
#ifdef USE_WEBSERVER
String dht_webPresent()
{
char stemp[10], sconv[10];
float t, h;
String page = "";
if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature as Celsius (the default)
snprintf_P(sconv, sizeof(sconv), PSTR("&deg;%c"), (TEMP_CONVERSION) ? 'F' : 'C');
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp);
page += F("<tr><td>DHT Temperature: </td><td>"); page += stemp; page += sconv; page += F("</td></tr>");
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp);
page += F("<tr><td>DHT Humidity: </td><td>"); page += stemp; page += F("%</td></tr>");
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_DHT

93
sonoff/xsns_dht2.ino Normal file
View File

@ -0,0 +1,93 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_DHT2
/*********************************************************************************************\
* DHT11, DHT21 (AM2301), DHT22 (AM2302, AM2321) - Temperature and Humidy
*
* Reading temperature or humidity takes about 250 milliseconds!
* Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
\*********************************************************************************************/
// WARNING: To use this DHT library you'll need to delete files DHT_U.cpp and DHT_U.h if present
#include "DHT.h"
DHT dht2(pin[GPIO_DHT11], dht_type);
float dht2_t, dht2_h = 0;
boolean dht_readTempHum(bool S, float &t, float &h)
{
h = dht2.readHumidity();
t = dht2.readTemperature(S);
if (!isnan(t)) dht2_t = t; else if (dht2_h) t = dht2_t;
if (!isnan(h)) dht2_h = h; else if (dht2_h) h = dht2_h;
return (!isnan(t) && !isnan(h));
}
void dht_init()
{
dht2.begin();
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void dht_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
char stemp1[10], stemp2[10];
float t, h;
if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2);
snprintf_P(svalue, ssvalue, PSTR("%s, \"DHT\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\"}"), svalue, stemp1, stemp2);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor2(stemp1, stemp2);
#endif // USE_DOMOTICZ
}
}
#ifdef USE_WEBSERVER
String dht_webPresent()
{
char stemp[10], sconv[10];
float t, h;
String page = "";
if (dht_readTempHum(TEMP_CONVERSION, t, h)) { // Read temperature as Celsius (the default)
snprintf_P(sconv, sizeof(sconv), PSTR("&deg;%c"), (TEMP_CONVERSION) ? 'F' : 'C');
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp);
page += F("<tr><td>DHT Temperature: </td><td>"); page += stemp; page += sconv; page += F("</td></tr>");
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp);
page += F("<tr><td>DHT Humidity: </td><td>"); page += stemp; page += F("%</td></tr>");
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_DHT2

206
sonoff/xsns_ds18b20.ino Normal file
View File

@ -0,0 +1,206 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_DS18B20
/*********************************************************************************************\
* DS18B20 - Temperature
*
* Source: Marinus vd Broek https://github.com/ESP8266nu/ESPEasy and AlexTransit (CRC)
\*********************************************************************************************/
uint8_t dsb_reset()
{
uint8_t r;
uint8_t retries = 125;
pinMode(pin[GPIO_DSB], INPUT);
do { // wait until the wire is high... just in case
if (--retries == 0) return 0;
delayMicroseconds(2);
} while (!digitalRead(pin[GPIO_DSB]));
pinMode(pin[GPIO_DSB], OUTPUT);
digitalWrite(pin[GPIO_DSB], LOW);
delayMicroseconds(492); // Dallas spec. = Min. 480uSec. Arduino 500uSec.
pinMode(pin[GPIO_DSB], INPUT); // Float
delayMicroseconds(40);
r = !digitalRead(pin[GPIO_DSB]);
delayMicroseconds(420);
return r;
}
uint8_t dsb_read_bit(void)
{
uint8_t r;
pinMode(pin[GPIO_DSB], OUTPUT);
digitalWrite(pin[GPIO_DSB], LOW);
delayMicroseconds(3);
pinMode(pin[GPIO_DSB], INPUT); // let pin float, pull up will raise
delayMicroseconds(10);
r = digitalRead(pin[GPIO_DSB]);
delayMicroseconds(53);
return r;
}
uint8_t dsb_read(void)
{
uint8_t bitMask;
uint8_t r = 0;
for (bitMask = 0x01; bitMask; bitMask <<= 1)
if (dsb_read_bit()) r |= bitMask;
return r;
}
void dsb_write_bit(uint8_t v)
{
if (v & 1) {
digitalWrite(pin[GPIO_DSB], LOW);
pinMode(pin[GPIO_DSB], OUTPUT);
delayMicroseconds(10);
digitalWrite(pin[GPIO_DSB], HIGH);
delayMicroseconds(55);
} else {
digitalWrite(pin[GPIO_DSB], LOW);
pinMode(pin[GPIO_DSB], OUTPUT);
delayMicroseconds(65);
digitalWrite(pin[GPIO_DSB], HIGH);
delayMicroseconds(5);
}
}
void dsb_write(uint8_t ByteToWrite)
{
uint8_t bitMask;
for (bitMask = 0x01; bitMask; bitMask <<= 1)
dsb_write_bit((bitMask & ByteToWrite) ? 1 : 0);
}
uint8 dsb_crc(uint8 inp, uint8 crc)
{
inp ^= crc;
crc = 0;
if (inp & 0x1) crc ^= 0x5e;
if (inp & 0x2) crc ^= 0xbc;
if (inp & 0x4) crc ^= 0x61;
if (inp & 0x8) crc ^= 0xc2;
if (inp & 0x10) crc ^= 0x9d;
if (inp & 0x20) crc ^= 0x23;
if (inp & 0x40) crc ^= 0x46;
if (inp & 0x80) crc ^= 0x8c;
return crc;
}
void dsb_readTempPrep()
{
dsb_reset();
dsb_write(0xCC); // Skip ROM
dsb_write(0x44); // Start conversion
}
float dsb_convertCtoF(float c)
{
return c * 1.8 + 32;
}
boolean dsb_readTemp(bool S, float &t)
{
int16_t DSTemp;
byte msb, lsb, crc;
t = NAN;
if (!dsb_read_bit()) { //check measurement end
addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor busy"));
return false;
}
/*
dsb_reset();
dsb_write(0xCC); // Skip ROM
dsb_write(0x44); // Start conversion
delay(800);
*/
dsb_reset();
dsb_write(0xCC); // Skip ROM
dsb_write(0xBE); // Read scratchpad
lsb = dsb_read();
msb = dsb_read();
crc = dsb_crc(lsb, crc);
crc = dsb_crc(msb, crc);
crc = dsb_crc(dsb_read(), crc);
crc = dsb_crc(dsb_read(), crc);
crc = dsb_crc(dsb_read(), crc);
crc = dsb_crc(dsb_read(), crc);
crc = dsb_crc(dsb_read(), crc);
crc = dsb_crc(dsb_read(), crc);
crc = dsb_crc(dsb_read(), crc);
dsb_reset();
if (crc) { //check crc
addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor CRC error"));
} else {
DSTemp = (msb << 8) + lsb;
t = (float(DSTemp) * 0.0625);
if(S) t = dsb_convertCtoF(t);
}
return !isnan(t);
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void dsb_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
char stemp1[10];
float t;
if (dsb_readTemp(TEMP_CONVERSION, t)) { // Check if read failed
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
snprintf_P(svalue, ssvalue, PSTR("%s, \"DS18B20\":{\"Temperature\":\"%s\"}"), svalue, stemp1);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor1(stemp1);
#endif // USE_DOMOTICZ
}
}
#ifdef USE_WEBSERVER
String dsb_webPresent()
{
// Needs TelePeriod to refresh data (Do not do it here as it takes too much time)
char stemp[10], sconv[10];
float st;
String page = "";
if (dsb_readTemp(TEMP_CONVERSION, st)) { // Check if read failed
snprintf_P(sconv, sizeof(sconv), PSTR("&deg;%c"), (TEMP_CONVERSION) ? 'F' : 'C');
dtostrf(st, 1, TEMP_RESOLUTION &3, stemp);
page += F("<tr><td>DSB Temperature: </td><td>"); page += stemp; page += sconv; page += F("</td></tr>");
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_DS18B20

201
sonoff/xsns_ds18x20.ino Normal file
View File

@ -0,0 +1,201 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_DS18x20
/*********************************************************************************************\
* DS18B20 - Temperature
\*********************************************************************************************/
#define W1_SKIP_ROM 0xCC
#define W1_CONVERT_TEMP 0x44
#define W1_READ_SCRATCHPAD 0xBE
#define DS18X20_MAX_SENSORS 8
#include <OneWire.h>
OneWire ds(pin[GPIO_DSB]);
uint8_t ds18x20_addr[DS18X20_MAX_SENSORS][8];
uint8_t ds18x20_idx[DS18X20_MAX_SENSORS];
uint8_t ds18x20_snsrs = 0;
void ds18x20_search()
{
uint8_t num_sensors=0;
uint8_t sensor = 0;
uint8_t i;
ds.reset_search();
for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors)
{
if (!ds.search(ds18x20_addr[num_sensors])) {
ds.reset_search();
break;
}
// If CRC Ok and Type DS18S20 or DS18B20
if ((OneWire::crc8(ds18x20_addr[num_sensors], 7) == ds18x20_addr[num_sensors][7]) &&
((ds18x20_addr[num_sensors][0]==0x10) || (ds18x20_addr[num_sensors][0]==0x28)))
num_sensors++;
}
for (int i = 0; i < num_sensors; i++) ds18x20_idx[i] = i;
for (int i = 0; i < num_sensors; i++) {
for (int j = i + 1; j < num_sensors; j++) {
if (uint32_t(ds18x20_addr[ds18x20_idx[i]]) > uint32_t(ds18x20_addr[ds18x20_idx[j]])) {
std::swap(ds18x20_idx[i], ds18x20_idx[j]);
}
}
}
ds18x20_snsrs = num_sensors;
}
uint8_t ds18x20_sensors()
{
return ds18x20_snsrs;
}
String ds18x20_address(uint8_t sensor)
{
char addrStr[20];
uint8_t i;
for (i = 0; i < 8; i++) sprintf(addrStr+2*i, "%02X", ds18x20_addr[ds18x20_idx[sensor]][i]);
return String(addrStr);
}
String ds18x20_type(uint8_t sensor)
{
char typeStr[10];
switch(ds18x20_addr[ds18x20_idx[sensor]][0]) {
case 0x10:
strcpy(typeStr, "DS18S20");
break;
case 0x28:
strcpy(typeStr, "DS18B20");
break;
default:
strcpy(typeStr, "DS18x20");
}
return String(typeStr);
}
void ds18x20_convert()
{
ds.reset();
ds.write(W1_SKIP_ROM); // Address all Sensors on Bus
ds.write(W1_CONVERT_TEMP); // start conversion, no parasite power on at the end
// delay(750); // 750ms should be enough for 12bit conv
}
float ds18x20_convertCtoF(float c)
{
return c * 1.8 + 32;
}
boolean ds18x20_read(uint8_t sensor, bool S, float &t)
{
byte data[12];
uint8_t sign = 1;
uint8_t i = 0;
float temp9 = 0.0;
uint8_t present = 0;
t = NAN;
ds.reset();
ds.select(ds18x20_addr[ds18x20_idx[sensor]]);
ds.write(W1_READ_SCRATCHPAD); // Read Scratchpad
for (i = 0; i < 9; i++) data[i] = ds.read();
if (OneWire::crc8(data, 8) == data[8]) {
switch(ds18x20_addr[ds18x20_idx[sensor]][0]) {
case 0x10: // DS18S20
if (data[1] > 0x80) sign = -1; // App-Note fix possible sign error
if (data[0] & 1) {
temp9 = ((data[0] >> 1) + 0.5) * sign;
} else {
temp9 = (data[0] >> 1) * sign;
}
t = (temp9 - 0.25) + ((16.0 - data[6]) / 16.0);
if(S) t = ds18x20_convertCtoF(t);
break;
case 0x28: // DS18B20
t = ((data[1] << 8) + data[0]) * 0.0625;
if(S) t = ds18x20_convertCtoF(t);
break;
}
}
return (!isnan(t));
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void ds18x20_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
char stemp1[10], stemp2[10];
float t;
byte dsxflg = 0;
for (byte i = 0; i < ds18x20_sensors(); i++) {
if (ds18x20_read(i, TEMP_CONVERSION, t)) { // Check if read failed
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp2);
if (!dsxflg) {
snprintf_P(svalue, ssvalue, PSTR("%s, \"DS18x20\":{"), svalue);
*djson = 1;
stemp1[0] = '\0';
}
dsxflg++;
snprintf_P(svalue, ssvalue, PSTR("%s%s\"DS%d\":{\"Type\":\"%s\", \"Address\":\"%s\", \"Temperature\":\"%s\"}"),
svalue, stemp1, i +1, ds18x20_type(i).c_str(), ds18x20_address(i).c_str(), stemp2);
strcpy(stemp1, ", ");
#ifdef USE_DOMOTICZ
if (dsxflg == 1) domoticz_sensor1(stemp2);
#endif // USE_DOMOTICZ
}
}
if (dsxflg) snprintf_P(svalue, ssvalue, PSTR("%s}"), svalue);
}
#ifdef USE_WEBSERVER
String ds18x20_webPresent()
{
char stemp[10], sconv[10];
float t;
String page = "";
snprintf_P(sconv, sizeof(sconv), PSTR("&deg;%c"), (TEMP_CONVERSION) ? 'F' : 'C');
for (byte i = 0; i < ds18x20_sensors(); i++) {
if (ds18x20_read(i, TEMP_CONVERSION, t)) { // Check if read failed
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp);
page += F("<tr><td>DS"); page += String(i +1); page += F(" Temperature: </td><td>"); page += stemp; page += sconv; page += F("</td></tr>");
}
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_DS18x20

412
sonoff/xsns_hlw8012.ino Normal file
View File

@ -0,0 +1,412 @@
/*
Copyright (c) 2017 Theo Arends. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*********************************************************************************************\
* HLW8012 - Energy
*
* Based on Source: Shenzhen Heli Technology Co., Ltd
\*********************************************************************************************/
#define HLW_PREF 10000 // 1000.0W
#define HLW_UREF 2200 // 220.0V
#define HLW_IREF 4545 // 4.545A
byte hlw_SELflag, hlw_cf_timer, hlw_cf1_timer, hlw_fifth_second, hlw_startup;
unsigned long hlw_cf_plen, hlw_cf_last;
unsigned long hlw_cf1_plen, hlw_cf1_last, hlw_cf1_ptot, hlw_cf1_pcnt, hlw_cf1u_plen, hlw_cf1i_plen;
unsigned long hlw_Ecntr, hlw_EDcntr, hlw_kWhtoday;
uint32_t hlw_lasttime;
unsigned long hlw_cf1u_pcntmax, hlw_cf1i_pcntmax;
Ticker tickerHLW;
#ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception
void hlw_cf_interrupt() ICACHE_RAM_ATTR;
void hlw_cf1_interrupt() ICACHE_RAM_ATTR;
#endif // USE_WS2812_DMA
void hlw_cf_interrupt() // Service Power
{
hlw_cf_plen = micros() - hlw_cf_last;
hlw_cf_last = micros();
if (hlw_cf_plen > 4000000) hlw_cf_plen = 0; // Just powered on
hlw_cf_timer = 15; // Support down to 4W which takes about 3 seconds
hlw_EDcntr++;
hlw_Ecntr++;
}
void hlw_cf1_interrupt() // Service Voltage and Current
{
hlw_cf1_plen = micros() - hlw_cf1_last;
hlw_cf1_last = micros();
if ((hlw_cf1_timer > 2) && (hlw_cf1_timer < 8)) { // Allow for 300 mSec set-up time and measure for up to 1 second
hlw_cf1_ptot += hlw_cf1_plen;
hlw_cf1_pcnt++;
if (hlw_cf1_pcnt == 10) hlw_cf1_timer = 8; // We need up to ten samples within 1 second (low current could take up to 0.3 second)
}
}
void hlw_200mS()
{
unsigned long hlw_len, hlw_temp;
hlw_fifth_second++;
if (hlw_fifth_second == 5) {
hlw_fifth_second = 0;
if (hlw_EDcntr) {
hlw_len = 1000000 / hlw_EDcntr;
hlw_EDcntr = 0;
hlw_temp = (HLW_PREF * sysCfg.hlw_pcal) / hlw_len;
hlw_kWhtoday += (hlw_temp * 100) / 36;
}
if (rtc_loctime() == rtc_midnight()) {
sysCfg.hlw_kWhyesterday = hlw_kWhtoday;
hlw_kWhtoday = 0;
}
if (hlw_startup && rtcTime.Valid && (rtcTime.DayOfYear == sysCfg.hlw_kWhdoy)) {
hlw_kWhtoday = sysCfg.hlw_kWhtoday;
hlw_startup = 0;
}
}
if (hlw_cf_timer) {
hlw_cf_timer--;
if (!hlw_cf_timer) hlw_cf_plen = 0; // No load for over three seconds
}
hlw_cf1_timer++;
if (hlw_cf1_timer >= 8) {
hlw_cf1_timer = 0;
hlw_SELflag = (hlw_SELflag) ? 0 : 1;
digitalWrite(pin[GPIO_HLW_SEL], hlw_SELflag);
if (hlw_cf1_pcnt) {
hlw_cf1_plen = hlw_cf1_ptot / hlw_cf1_pcnt;
} else {
hlw_cf1_plen = 0;
}
if (hlw_SELflag) {
hlw_cf1u_plen = hlw_cf1_plen;
hlw_cf1u_pcntmax = hlw_cf1_pcnt;
} else {
hlw_cf1i_plen = hlw_cf1_plen;
hlw_cf1i_pcntmax = hlw_cf1_pcnt;
}
hlw_cf1_ptot = 0;
hlw_cf1_pcnt = 0;
}
}
void hlw_savestate()
{
sysCfg.hlw_kWhdoy = (rtcTime.Valid) ? rtcTime.DayOfYear : 0;
sysCfg.hlw_kWhtoday = hlw_kWhtoday;
}
boolean hlw_readEnergy(byte option, float &ed, uint16_t &e, uint16_t &w, uint16_t &u, float &i, float &c)
{
unsigned long hlw_len, hlw_temp, hlw_w, hlw_u, hlw_i;
int hlw_period, hlw_interval;
//char log[LOGSZ];
//snprintf_P(log, sizeof(log), PSTR("HLW: CF %d, CF1U %d (%d), CF1I %d (%d)"), hlw_cf_plen, hlw_cf1u_plen, hlw_cf1u_pcntmax, hlw_cf1i_plen, hlw_cf1i_pcntmax);
//addLog(LOG_LEVEL_DEBUG, log);
if (hlw_kWhtoday) {
ed = (float)hlw_kWhtoday / 100000000;
} else {
ed = 0;
}
if (option) {
if (!hlw_lasttime) {
hlw_period = sysCfg.tele_period;
} else {
hlw_period = rtc_loctime() - hlw_lasttime;
}
hlw_lasttime = rtc_loctime();
hlw_interval = 3600 / hlw_period;
if (hlw_Ecntr) {
hlw_len = hlw_period * 1000000 / hlw_Ecntr;
hlw_Ecntr = 0;
hlw_temp = ((HLW_PREF * sysCfg.hlw_pcal) / hlw_len) / hlw_interval;
e = hlw_temp / 10;
} else {
e = 0;
}
}
if (hlw_cf_plen) {
hlw_w = (HLW_PREF * sysCfg.hlw_pcal) / hlw_cf_plen;
w = hlw_w / 10;
} else {
w = 0;
}
if (hlw_cf1u_plen && (w || (power &1))) {
hlw_u = (HLW_UREF * sysCfg.hlw_ucal) / hlw_cf1u_plen;
u = hlw_u / 10;
} else {
u = 0;
}
if (hlw_cf1i_plen && w) {
hlw_i = (HLW_IREF * sysCfg.hlw_ical) / hlw_cf1i_plen;
i = (float)hlw_i / 1000;
} else {
i = 0;
}
if (hlw_i && hlw_u && hlw_w && w) {
hlw_temp = (hlw_w * 100) / ((hlw_u * hlw_i) / 1000);
if (hlw_temp > 100) {
hlw_temp = 100;
}
c = (float)hlw_temp / 100;
} else {
c = 0;
}
return true;
}
void hlw_init()
{
if (!sysCfg.hlw_pcal || (sysCfg.hlw_pcal == 4975)) {
sysCfg.hlw_pcal = HLW_PREF_PULSE;
sysCfg.hlw_ucal = HLW_UREF_PULSE;
sysCfg.hlw_ical = HLW_IREF_PULSE;
}
hlw_cf_plen = 0;
hlw_cf_last = 0;
hlw_cf1_plen = 0;
hlw_cf1_last = 0;
hlw_cf1u_plen = 0;
hlw_cf1i_plen = 0;
hlw_cf1u_pcntmax = 0;
hlw_cf1i_pcntmax = 0;
hlw_Ecntr = 0;
hlw_EDcntr = 0;
hlw_kWhtoday = 0;
hlw_SELflag = 0; // Voltage;
pinMode(pin[GPIO_HLW_SEL], OUTPUT);
digitalWrite(pin[GPIO_HLW_SEL], hlw_SELflag);
pinMode(pin[GPIO_HLW_CF1], INPUT_PULLUP);
attachInterrupt(pin[GPIO_HLW_CF1], hlw_cf1_interrupt, FALLING);
pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP);
attachInterrupt(pin[GPIO_HLW_CF], hlw_cf_interrupt, FALLING);
hlw_startup = 1;
hlw_lasttime = 0;
hlw_fifth_second = 0;
hlw_cf_timer = 0;
hlw_cf1_timer = 0;
tickerHLW.attach_ms(200, hlw_200mS);
}
/********************************************************************************************/
boolean hlw_margin(byte type, uint16_t margin, uint16_t value, byte &flag, byte &saveflag)
{
byte change;
if (!margin) return false;
change = saveflag;
if (type) {
flag = (value > margin);
} else {
flag = (value < margin);
}
saveflag = flag;
return (change != saveflag);
}
void hlw_margin_chk()
{
char log[LOGSZ], stopic[TOPSZ], svalue[MESSZ];
float ped, pi, pc;
uint16_t uped, piv, pe, pw, pu;
byte flag, jsonflg;
if (power_steady_cntr) {
power_steady_cntr--;
return;
}
hlw_readEnergy(0, ped, pe, pw, pu, pi, pc);
if (power && (sysCfg.hlw_pmin || sysCfg.hlw_pmax || sysCfg.hlw_umin || sysCfg.hlw_umax || sysCfg.hlw_imin || sysCfg.hlw_imax)) {
piv = (uint16_t)(pi * 1000);
// snprintf_P(log, sizeof(log), PSTR("HLW: W %d, U %d, I %d"), pw, pu, piv);
// addLog(LOG_LEVEL_DEBUG, log);
snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/TELEMETRY"), PUB_PREFIX2, sysCfg.mqtt_topic);
snprintf_P(svalue, sizeof(svalue), PSTR("{"));
jsonflg = 0;
if (hlw_margin(0, sysCfg.hlw_pmin, pw, flag, hlw_pminflg)) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"PowerLow\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF);
jsonflg = 1;
}
if (hlw_margin(1, sysCfg.hlw_pmax, pw, flag, hlw_pmaxflg)) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"PowerHigh\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF);
jsonflg = 1;
}
if (hlw_margin(0, sysCfg.hlw_umin, pu, flag, hlw_uminflg)) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"VoltageLow\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF);
jsonflg = 1;
}
if (hlw_margin(1, sysCfg.hlw_umax, pw, flag, hlw_umaxflg)) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"VoltageHigh\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF);
jsonflg = 1;
}
if (hlw_margin(0, sysCfg.hlw_imin, piv, flag, hlw_iminflg)) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"CurrentLow\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF);
jsonflg = 1;
}
if (hlw_margin(1, sysCfg.hlw_imax, piv, flag, hlw_imaxflg)) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"CurrentHigh\":\"%s\""), svalue, (jsonflg)?", ":"", (flag) ? MQTT_STATUS_ON : MQTT_STATUS_OFF);
jsonflg = 1;
}
if (jsonflg) {
snprintf_P(svalue, sizeof(svalue), PSTR("%s}"), svalue);
mqtt_publish(stopic, svalue);
}
}
#ifdef FEATURE_POWER_LIMIT
snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/RESULT"), PUB_PREFIX, sysCfg.mqtt_topic);
// Max Power
if (sysCfg.hlw_mpl) {
if (pw > sysCfg.hlw_mpl) {
if (!hlw_mplh_counter) {
hlw_mplh_counter = sysCfg.hlw_mplh;
} else {
hlw_mplh_counter--;
if (!hlw_mplh_counter) {
snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxPowerReached\":\"%d%s\"}"), pw, (sysCfg.value_units) ? " W" : "");
mqtt_publish(stopic, svalue);
do_cmnd_power(1, 0);
if (!hlw_mplr_counter) hlw_mplr_counter = MAX_POWER_RETRY +1;
hlw_mplw_counter = sysCfg.hlw_mplw;
}
}
}
else if (power && (pw <= sysCfg.hlw_mpl)) {
hlw_mplh_counter = 0;
hlw_mplr_counter = 0;
hlw_mplw_counter = 0;
}
if (!power) {
if (hlw_mplw_counter) {
hlw_mplw_counter--;
} else {
if (hlw_mplr_counter) {
hlw_mplr_counter--;
if (hlw_mplr_counter) {
snprintf_P(svalue, sizeof(stopic), PSTR("{\"PowerMonitor\":\"%s\"}"), MQTT_STATUS_ON);
mqtt_publish(stopic, svalue);
do_cmnd_power(1, 1);
} else {
snprintf_P(svalue, sizeof(stopic), PSTR("{\"MaxPowerReachedRetry\":\"%s\"}"), MQTT_STATUS_OFF);
mqtt_publish(stopic, svalue);
}
}
}
}
}
// Max Energy
if (sysCfg.hlw_mkwh) {
uped = (uint16_t)(ped * 1000);
if (!hlw_mkwh_state && (rtcTime.Hour == sysCfg.hlw_mkwhs)) {
hlw_mkwh_state = 1;
snprintf_P(svalue, sizeof(stopic), PSTR("{\"EnergyMonitor\":\"%s\"}"), MQTT_STATUS_ON);
mqtt_publish(stopic, svalue);
do_cmnd_power(1, 1);
}
else if ((hlw_mkwh_state == 1) && (uped >= sysCfg.hlw_mkwh)) {
hlw_mkwh_state = 2;
dtostrf(ped, 1, 3, svalue);
snprintf_P(svalue, sizeof(svalue), PSTR("{\"MaxEnergyReached\":\"%s%s\"}"), svalue, (sysCfg.value_units) ? " kWh" : "");
mqtt_publish(stopic, svalue);
do_cmnd_power(1, 0);
}
}
#endif // FEATURE_POWER_LIMIT
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void hlw_mqttPresent(uint8_t domidx)
{
char stopic[TOPSZ], svalue[MESSZ], stime[21], stemp0[10], stemp1[10], stemp2[10], stemp3[10];
float ped, pi, pc;
uint16_t pe, pw, pu;
snprintf_P(stime, sizeof(stime), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"),
rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second);
hlw_readEnergy(1, ped, pe, pw, pu, pi, pc);
dtostrf((float)sysCfg.hlw_kWhyesterday / 100000000, 1, 3, stemp0);
dtostrf(ped, 1, 3, stemp1);
dtostrf(pc, 1, 2, stemp2);
dtostrf(pi, 1, 3, stemp3);
snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/TELEMETRY"), PUB_PREFIX2, sysCfg.mqtt_topic);
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Time\":\"%s\", \"Energy\":{\"Yesterday\":\"%s\", \"Today\":\"%s\", \"Period\":%d, \"Power\":%d, \"Factor\":\"%s\", \"Voltage\":%d, \"Current\":\"%s\"}}"),
stime, stemp0, stemp1, pe, pw, stemp2, pu, stemp3);
mqtt_publish(stopic, svalue);
#ifdef USE_DOMOTICZ
dtostrf(ped * 1000, 1, 1, stemp1);
domoticz_sensor4(pw, stemp1);
#endif // USE_DOMOTICZ
}
#ifdef USE_WEBSERVER
String hlw_webPresent()
{
char stemp[10];
float ped, pi, pc;
uint16_t pe, pw, pu;
String page = "";
hlw_readEnergy(0, ped, pe, pw, pu, pi, pc);
page += F("<tr><td>Voltage: </td><td>"); page += String(pu); page += F(" V</td></tr>");
dtostrf(pi, 1, 3, stemp);
page += F("<tr><td>Current: </td><td>"); page += stemp; page += F(" A</td></tr>");
page += F("<tr><td>Power: </td><td>"); page += String(pw); page += F(" W</td></tr>");
dtostrf(pc, 1, 2, stemp);
page += F("<tr><td>Power Factor: </td><td>"); page += stemp; page += F("</td></tr>");
dtostrf(ped, 1, 3, stemp);
page += F("<tr><td>Energy Today: </td><td>"); page += stemp; page += F(" kWh</td></tr>");
dtostrf((float)sysCfg.hlw_kWhyesterday / 100000000, 1, 3, stemp);
page += F("<tr><td>Energy Yesterday: </td><td>"); page += stemp; page += F(" kWh</td></tr>");
return page;
}
#endif // USE_WEBSERVER

271
sonoff/xsns_htu21.ino Normal file
View File

@ -0,0 +1,271 @@
/*
Copyright (c) 2017 Heiko Krupp. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef USE_I2C
#ifdef USE_HTU
/*********************************************************************************************\
* HTU21 - Temperature and Humidy
*
* Source: Heiko Krupp
\*********************************************************************************************/
#define HTU21_ADDR 0x40
#define HTU21_CHIPID 0x32
#define HTU21_READTEMP 0xE3
#define HTU21_READHUM 0xE5
#define HTU21_WRITEREG 0xE6
#define HTU21_READREG 0xE7
#define HTU21_RESET 0xFE
#define HTU21_HEATER_WRITE 0x51
#define HTU21_HEATER_READ 0x11
#define HTU21_SERIAL2_READ1 0xFC /* Read 3rd two Serial bytes */
#define HTU21_SERIAL2_READ2 0xC9 /* Read 4th two Serial bytes */
#define HTU21_HEATER_ON 0x04
#define HTU21_HEATER_OFF 0xFB
#define HTU21_RES_RH12_T14 0x00 // Default
#define HTU21_RES_RH8_T12 0x01
#define HTU21_RES_RH10_T13 0x80
#define HTU21_RES_RH11_T11 0x81
#define HTU21_MAX_HUM 16 // 16ms max time
#define HTU21_MAX_TEMP 50 // 50ms max time
#define HTU21_CRC8_POLYNOM 0x13100
uint8_t htuaddr, htutype = 0;
char htustype[7];
uint8_t check_crc8(uint16_t data)
{
for (uint8_t bit = 0; bit < 16; bit++)
{
if (data & 0x8000)
data = (data << 1) ^ HTU21_CRC8_POLYNOM;
else
data <<= 1;
}
return data >>= 8;
}
uint8_t htu21_readDeviceID(void)
{
uint16_t deviceID = 0;
uint8_t checksum = 0;
Wire.beginTransmission(HTU21_ADDR);
Wire.write(HTU21_SERIAL2_READ1);
Wire.write(HTU21_SERIAL2_READ2);
Wire.endTransmission();
Wire.requestFrom(HTU21_ADDR, 3);
deviceID = Wire.read() << 8;
deviceID |= Wire.read();
checksum = Wire.read();
if (check_crc8(deviceID) == checksum) {
deviceID = deviceID >> 8;
} else {
deviceID = 0;
}
return (uint8_t)deviceID;
}
void htu21_setRes(uint8_t resolution)
{
uint8_t current = i2c_read8(HTU21_ADDR, HTU21_READREG);
current &= 0x7E; // Replace current resolution bits with 0
current |= resolution; // Add new resolution bits to register
i2c_write8(HTU21_ADDR, HTU21_WRITEREG, current);
}
void htu21_reset(void)
{
Wire.beginTransmission(HTU21_ADDR);
Wire.write(HTU21_RESET);
Wire.endTransmission();
delay(15); // Reset takes 15ms
}
void htu21_heater(uint8_t heater)
{
uint8_t current = i2c_read8(HTU21_ADDR, HTU21_READREG);
switch(heater)
{
case HTU21_HEATER_ON : current |= heater;
break;
case HTU21_HEATER_OFF : current &= heater;
break;
default : current &= heater;
break;
}
i2c_write8(HTU21_ADDR, HTU21_WRITEREG, current);
}
boolean htu21_init()
{
htu21_reset();
htu21_heater(HTU21_HEATER_OFF);
htu21_setRes(HTU21_RES_RH12_T14);
return true;
}
float htu21_convertCtoF(float c)
{
return c * 1.8 + 32;
}
float htu21_readHumidity(void)
{
uint8_t checksum=0;
uint16_t sensorval=0;
float humidity=0.0;
Wire.beginTransmission(HTU21_ADDR);
Wire.write(HTU21_READHUM);
if(Wire.endTransmission() != 0) return 0.0; // In case of error
delay(HTU21_MAX_HUM); // HTU21 time at max resolution
Wire.requestFrom(HTU21_ADDR, 3);
if(3 <= Wire.available())
{
sensorval = Wire.read() << 8; // MSB
sensorval |= Wire.read(); // LSB
checksum = Wire.read();
}
if(check_crc8(sensorval) != checksum) return 0.0; // Checksum mismatch
sensorval ^= 0x02; // clear status bits
humidity = 0.001907 * (float)sensorval - 6;
if(humidity>100) return 100.0;
if(humidity<0) return 0.01;
return humidity;
}
float htu21_readTemperature(bool S)
{
uint8_t checksum=0;
uint16_t sensorval=0;
float t;
Wire.beginTransmission(HTU21_ADDR);
Wire.write(HTU21_READTEMP);
if(Wire.endTransmission() != 0) return 0.0; // In case of error
delay(HTU21_MAX_TEMP); // HTU21 time at max resolution
Wire.requestFrom(HTU21_ADDR, 3);
if(3 == Wire.available())
{
sensorval = Wire.read() << 8; // MSB
sensorval |= Wire.read(); // LSB
checksum = Wire.read();
}
if(check_crc8(sensorval) != checksum) return 0.0; // Checksum mismatch
t = (0.002681 * (float)sensorval - 46.85);
if(S) t = htu21_convertCtoF(t);
return t;
}
float htu21_compensatedHumidity(float humidity, float temperature)
{
if(humidity == 0.00 && temperature == 0.00) return 0.0;
if(temperature > 0.00 && temperature < 80.00)
return (-0.15)*(25-temperature)+humidity;
}
uint8_t htu_detect()
{
if (htutype) return true;
char log[LOGSZ];
boolean success = false;
htuaddr = HTU21_ADDR;
htutype = htu21_readDeviceID();
snprintf_P(htustype, sizeof(htustype), PSTR("HTU"));
switch (htutype) {
case HTU21_CHIPID:
success = htu21_init();
snprintf_P(htustype, sizeof(htustype), PSTR("HTU21"));
}
if (success) {
snprintf_P(log, sizeof(log), PSTR("I2C: %s found at address 0x%x"), htustype, htuaddr);
addLog(LOG_LEVEL_DEBUG, log);
} else {
htutype = 0;
}
return success;
}
/*********************************************************************************************\
* Presentation
\*********************************************************************************************/
void htu_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson, uint8_t domidx)
{
if (!htutype) return;
char stemp1[10], stemp2[10];
float t = htu21_readTemperature(TEMP_CONVERSION);
float h = htu21_readHumidity();
h = htu21_compensatedHumidity(h, t);
dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1);
dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2);
snprintf_P(svalue, ssvalue, PSTR("%s, \"%s\":{\"Temperature\":\"%s\", \"Humidity\":\"%s\"}"), svalue, htustype, stemp1, stemp2);
*djson = 1;
#ifdef USE_DOMOTICZ
domoticz_sensor2(stemp1, stemp2);
#endif // USE_DOMOTICZ
}
#ifdef USE_WEBSERVER
String htu_webPresent()
{
String page = "";
if (htutype) {
char itemp[10], iconv[10];
snprintf_P(iconv, sizeof(iconv), PSTR("&deg;%c"), (TEMP_CONVERSION) ? 'F' : 'C');
float t_htu21 = htu21_readTemperature(TEMP_CONVERSION);
float h_htu21 = htu21_readHumidity();
h_htu21 = htu21_compensatedHumidity(h_htu21, t_htu21);
dtostrf(t_htu21, 1, TEMP_RESOLUTION &3, itemp);
page += F("<tr><td>HTU Temperature: </td><td>"); page += itemp; page += iconv; page += F("</td></tr>");
dtostrf(h_htu21, 1, HUMIDITY_RESOLUTION &3, itemp);
page += F("<tr><td>HTU Humidity: </td><td>"); page += itemp; page += F("%</td></tr>");
}
return page;
}
#endif // USE_WEBSERVER
#endif // USE_HTU
#endif // USE_I2C